错误处理

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()
}

Info.plist setup

默认的 TextEmailer 期望您的电子邮件地址在应用程序的 Info.plist 中,键名为“SupportEmail”。只需添加此键和字符串值即可使其工作。

用法

Default Error Dialog

该框架包含

您只需调用

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 记录到数组

如果您碰巧使用 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 文件。