首先,这是一个很好的问题.错误处理是一项特定任务,适用于一系列令人难以置信的情况,他们知道应用程序状态会产生什么影响.关键问题是对您的用户,应用程序和开发人员有意义的事情.
我喜欢在概念上看到Responder链如何用于处理事件.就像遍历响应者链的事件一样,错误有可能冒出App的抽象级别.根据错误,您可能希望执行与错误类型相关的许多操作.您的应用程序的不同组件可能需要了解错误,它可能是一个错误,取决于应用程序的状态不需要任何操作.
您作为开发人员最终会知道哪些错误会影响您的应用以及如何.因此,我们如何选择实施技术解决方案.
我建议使用Enumerations和Closures 来构建我的错误处理解决方案.
这是一个ENUM的人为例子.如您所见,它代表了错误处理解决方案的核心.
public enum MyAppErrorCode { case NotStartedCode(Int, String) case ResponseOkCode case ServiceInProgressCode(Int, String) case ServiceCancelledCode(Int, String, NSError) func handleCode(errorCode: MyAppErrorCode) { switch(errorCode) { case NotStartedCode(let code, let message): print("code: \(code)") print("message: \(message)") case ResponseOkCode: break case ServiceInProgressCode(let code, let message): print("code: \(code)") print("message: \(message)") case ServiceCancelledCode(let code, let message, let error): print("code: \(code)") print("message: \(message)") print("error: \(error.localizedDescription)") } } }
接下来我们要定义我们的completionHandler,它将替换((error: NSError?) -> Void)
下载方法中的闭包.
((errorCode: MyAppErrorCode) -> Void)
新的下载功能
func download(destinationUrl: NSURL, completionHandler: ((errorCode: MyAppErrorCode) -> Void)) { let request = NSURLRequest(URL: resourceUrl!) let task = downloadSession.downloadTaskWithRequest(request) { (url: NSURL?, response: NSURLResponse?, error: NSError?) in if error == nil { do { try self.fileManager.moveItemAtURL(url!, toURL: destinationUrl) completionHandler(errorCode: MyAppErrorCode.ResponseOkCode) } catch let e { print(e) completionHandler(errorCode: MyAppErrorCode.MoveItemFailedCode(170, "Text you would like to display to the user..", e)) } } else { completionHandler(errorCode: MyAppErrorCode.DownloadFailedCode(404, "Text you would like to display to the user..")) } }.resume() }
在传入的闭包中,您可以调用handleCode(errorCode: MyAppErrorCode)
或在ENUM上定义的任何其他函数.
您现在可以使用组件来定义自己的错误处理解决方案,该解决方案可以轻松定制到您的应用程序,您可以使用它来将http代码和任何其他第三方错误/响应代码映射到应用程序中有意义的内容.您还可以选择让NSError冒泡是否有用.
回到我们的设计.
我们如何处理与视图控制器的交互?我们可以选择拥有现在的集中机制,或者我们可以在视图控制器中处理它并将范围保持在本地.为此,我们将逻辑从ENUM移动到视图控制器并定位视图控制器任务的非常具体的要求(在这种情况下下载),您也可以将ENUM移动到视图控制器的范围.我们实现了封装,但最轻微的是最终会在项目的其他地方重复我们的代码.无论哪种方式,您的视图控制器都必须对错误/结果代码执行某些操作
我更喜欢的方法是让视图控制器有机会处理完成处理程序中的特定行为,或者/然后将其传递给我们的ENUM以获得更一般的行为,例如发送下载已完成的通知,更新应用程序状态或只需使用"OK"的单个操作抛出AlertViewController.
我们通过向视图控制器添加方法来执行此操作,该方法可以传递MyAppErrorCode
ENUM和任何相关变量(URL,Request ...)并添加任何实例变量以跟踪我们的任务,即不同的URL或尝试次数在我们放弃尝试下载之前.
以下是在视图控制器上处理下载的可能方法:
func didCompleteDownloadWithResult(resultCode: MyAppErrorCode, request: NSURLRequest, url: NSURL) { switch(resultCode) { case .ResponseOkCode: // Made up method as an example resultCode.postSuccessfulDownloadNotification(url, dictionary: ["request" : request]) case .FailedDownloadCode(let code, let message, let error): if numberOfAttempts = maximumAttempts { // Made up method as an example finishedAttemptingDownload() } else { // Made up method as an example AttemptDownload(numberOfAttempts) } default: break } }