在我的应用程序中,我需要从 Web 下载一个 JSON 文件。我制作了一个 ResourceService
类,它有一个 download
方法,如下所示。我在我的应用程序的“更高级别”服务中使用此服务。您可以看到在下载过程中有很多地方可能出错。可能是服务器着火,暂时无法成功响应,也可能是临时文件移动过程中出现问题等。
现在,除了稍后尝试之外,用户可能对此无能为力。然而,他/她可能想知道出了点问题,下载或“更高级别”方法的行为无法成功。
我作为开发人员对此感到困惑,因为我不明白如何处理 Swift 中的错误。我有一个 completionHandler
,如果有错误,它会接受一个错误,但我不知道我应该将哪种错误传回给调用者。
想法:
1) 如果我传递从 NSFileManager API 或 NSURLSession API 获得的错误对象,我会认为我正在将 download
方法的一些实现“泄露”给调用者。调用者如何根据错误知道预期的错误类型?可能两者兼而有之。
2) 如果我应该捕获并包装那些可能在 download
方法中发生的错误,那会是什么样子?
3) 如何处理方法内的多个错误源,以及调用可能抛出/返回 NSError 对象的方法的代码是什么样的?
作为调用者,您是否应该开始拦截您返回的错误,然后编写大量代码来根据错误代码区分消息/采取的操作?我根本不明白这个错误处理的东西,也不知道当一个方法中有很多事情可能出错时它会是什么样子。
func download(destinationUrl: NSURL, completionHandler: ((error: NSError?) -> 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)
} catch let e {
print(e)
}
} else {
}
}.resume()
}
最佳答案
首先,这是一个很好的问题。错误处理是一项特定任务,适用于一系列令人难以置信的情况,谁知道会对您的应用程序状态产生什么影响。关键问题是什么对您的用户、应用和开发者您有意义。
我喜欢在概念上将其视为 Responder chain用于处理事件。就像遍历响应者链的事件一样,错误有可能使您的应用程序的抽象级别冒泡。根据错误的不同,您可能想要做一些与错误类型相关的事情。您的应用程序的不同组件可能需要了解错误,根据应用程序的状态,它可能不需要任何操作。
作为开发者,您最终知道错误会在何处影响您的应用以及如何影响。因此,鉴于我们如何选择实现技术解决方案。
我建议使用 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 冒泡是否有用。
编辑
回到我们的设计。
我们如何处理与 View Controller 的交互?我们可以选择像现在这样的集中机制,或者我们可以在 View Controller 中处理它并将范围保持在本地。为此,我们会将逻辑从 ENUM 移动到 View Controller ,并针对 View Controller 任务的非常具体的要求(在本例中为下载),您也可以将 ENUM 移动到 View Controller 的范围。我们实现了封装,但最终很可能会在项目的其他地方重复我们的代码。无论哪种方式,您的 View Controller 都必须对错误/结果代码做一些事情
我更喜欢的一种方法是让 View Controller 有机会处理完成处理程序中的特定行为,或者/然后将其传递给我们的 ENUM 以获得更一般的行为,例如发送下载已完成的通知、更新应用程序状态或只是通过“确定”的单个操作抛出 AlertViewController。
我们通过向我们的 View Controller 添加方法来实现这一点,这些方法可以传递 MyAppErrorCode
ENUM 和任何相关变量(URL、请求...)并添加任何实例变量来跟踪我们的任务,即不同的 URL,或者我们放弃尝试下载之前的尝试次数。
这是在 View Controller 处理下载的可能方法:
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
}
}
关于ios - 在 Swift 中处理错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34364986/