b 无论如何,每个程序迟早都会崩溃或出现异常。即使应用程序可以恢复,最好还是让用户尽早报告问题。
与其将问题推迟到以后...
do {
try managedObjectContext.save()
} catch let error as NSError {
fatalError("\(error)") // TODO report crash
}
... 不如直接使用这个微框架,并立即进行适当的处理。您可以稍后考虑如何报告错误。但现在您将知道错误已被处理
do {
try managedObjectContext.save()
} catch let error as NSError {
applicationError("Saving MOC failed: \(error)")
// Optionally help the user rescue the data:
// saveManagedObjectContextChangesToPlist(managedObjectContext)
return
}
在应用程序启动时设置报告器。您可以在启动期间执行此操作,或者直接在 AppDelegate 中调用它
import ErrorHandling
func bootstrapErrorReporting() {
ErrorAlert.emailer = TextEmailer()
}
默认的 TextEmailer
期望您的电子邮件地址在应用程序的 Info.plist
中,键名为“SupportEmail”。只需添加此键和字符串值即可使其工作。
该框架包含
ErrorAlert
,用于显示包含错误消息的警报,告知用户发生了什么。TextEmailer
是默认的错误处理程序。它只是使用 mailto:
协议通过系统的默认电子邮件程序发送邮件。您可以替换为任何符合 ReportEmailer
协议的内容。您只需调用
let error: NSError = ...
ErrorAlert(error: error).displayModal()
即可完成设置。
Report
类型允许您使用自定义字符串包装任何 Error
,该字符串应与报告电子邮件一起发送。
let error: Error = ...
let logs = previousLogMessages.joined(separator: "\n")
let report = Report(error: error, additionalInfo: logs)
ErrorAlert(report: report).displayModal()
如果您碰巧使用 SwiftyBeaver 进行日志记录,这里有一个 InMemoryDestination
,它可以跟踪过去的日志消息,以便您可以将它们随报告一起发送
import SwiftyBeaver
class InMemoryDestination: BaseDestination {
var maxHistory = 5
fileprivate(set) var messages: [String] = []
override func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, file: String, function: String, line: Int) -> String? {
let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line) ?? "\(msg) (formatting error!)"
messages = Array(messages
.appending(formattedString)
.suffix(maxHistory))
return formattedString
}
}
因为对于意外错误这样做很糟糕,所以我为我的项目添加了一些东西,这样我就可以简单地这样做
let error: NSError = ...
fatalApplicationError("Start-up failed: \(error)")
或者这样做,以处理我犯傻的情况
programmerError("Unhandled and unexpected exception!!11 \(error)")
或者这样做,当一些可能经常出错但尚未妥善处理的事情出错时
// Think of it like a feature request! :)
applicationError("File not found: \(error)")
这里有一些代码可以实现这一点,作为奖励
import Foundation
let errorHandler = ErrorHandler() // This is just for demonstration, of course
@noreturn func fatalApplicationError(message: String, function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__) {
applicationError(message, function: function, file: file, line: line)
fatalError(message)
}
func applicationError(message: String, function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__) {
let error = ErrorHandler.errorWithMessage(message, function: function, file: file, line: line)
errorHandler.handle(error)
}
func programmerError(message: String, function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__) {
let error = ErrorHandler.errorWithMessage("Program fault:\n \(message)", function: function, file: file, line: line)
errorHandler.handle(error)
}
public class ErrorHandler {
static let exceptionDomain = "de.christiantietze.MyNextApp.exception"
static var functionKey: String { return "\(exceptionDomain).function" }
static var fileKey: String { return "\(exceptionDomain).file" }
static var lineKey: String { return "\(exceptionDomain).line" }
public init() { }
}
extension ErrorHandler {
public func handle(error: NSError?) {
if let error = error {
logError(error)
reportError(error)
}
}
private func logError(error: NSError) {
NSLog("Error: \(error)")
}
private func reportError(error: NSError) {
ErrorAlert(error: error).displayModal()
}
public static func errorWithMessage(message: String? = nil, function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__) -> NSError {
var userInfo: [String: AnyObject] = [
functionKey: function,
fileKey: file,
lineKey: line,
]
if let message = message {
userInfo[NSLocalizedDescriptionKey] = message
}
return NSError(domain: exceptionDomain, code: 0, userInfo: userInfo)
}
}
该软件包不收集或存储任何数据。
TextEmailer
依赖于用户的电子邮件客户端向您的支持收件箱发送电子邮件,因此它根本不处理用户的电子邮件。
版权所有 (c) 2015 Christian Tietze。根据 MIT 许可证分发。
有关详细信息,请参阅 LICENSE 文件。