我正在为我的项目中使用的Web服务创建一个框架。我在GitHub上传了模板。 https://github.com/vivinjeganathan/ErrorHandling
它有各种层次。第1层用于验证。第2层用于形成请求。第3层用于实际网络呼叫。
查看控制器< ---->层1< --->层2< --->第3层
数据通过闭包在层之间流动,如果在任何层发生错误,则需要将它优雅地传递给ViewController。
我在异步调用中引用此链接进行错误处理 - http://appventure.me/2015/06/19/swift-try-catch-asynchronous-closures/ 在同一个repo-name中创建了一个分支 - ErrorHandling-Method1。
我能够将错误从第3层传输到第2层(单级 - 通过闭包中的函数返回响应 - 如链接中所述)。但是面对跨多层转移的困难。
任何人都可以协助公共GitHub中提供的示例应用程序吗?
答案 0 :(得分:3)
我个人会使用传递NSError的通知作为图层中通知的对象,并在视图控制器中观察通知。
在图层中:
NSNotificationCenter.defaultCenter().postNotificationName("ErrorEncounteredNotification", object: error)
在视图控制器中
NSNotificationCenter.defaultCenter().addObserver(self, selector: "errorEncountered:", name: "ErrorEncounteredNotification", object: nil)
选择器方法:
func errorEncountered(notification: NSNotification!) {
let error: NSError! = notification.object as! NSError
NSLog("error: \(error)")
}
答案 1 :(得分:3)
首先,我认为没有必要像你那样堆叠图层,例如,通过添加验证功能作为一个层,你正在增加耦合,使得该层依赖于下面的层(解析,网络)等等,为什么不将验证分开,使其仅依赖于数据?:
class ViewController: UIViewController {
var validator = InputValidator()
override func viewDidLoad() {
super.viewDidLoad()
do {
try validator.validateInput("INPUT")
try Fetcher.requestDataWithParams("INPUT")
}
catch {
handleError(error)
}
}
}
现在验证功能不依赖于其他层,因此通信将如下所示:
查看控制器< ---> ParsingLayer< ---> NetworkingLayer
我确实重命名了图层,但它们不一定要像这样,你可以添加或删除图层。
如果我尝试解释我的方法,我认为会有点复杂,所以我将使用前面的图层给出一个例子,首先是底层:
class NetworkingLayer {
class func requestData(params: AnyObject, completion: (getResult: () throw -> AnyObject) -> Void) -> Void {
session.dataTaskWithURL(url) { (data, urlResponse, var error) in
if let error = error {
completion(getResult: { throw error })
} else {
completion(getResult: { return data })
}
}
}
}
我省略了一些代码部分,但我们的想法是做任何必要的步骤来使图层工作(创建会话等)并始终通过完成闭包进行通信;顶部的图层如下所示:
class ParsingLayer {
class func requestObject(params: AnyObject, completion: (getObject: () throw -> CustomObject) -> Void) -> Void {
NetworkingLayer.requestData(params, completion: { (getResult) -> Void in
do {
let data = try getResult()
let object = try self.parseData(data)
completion(getObject: { return object })
}
catch {
completion(getObject: { throw error })
}
})
}
}
请注意,完成闭包不一样,因为每个层都添加了功能,返回的对象可以更改,还注意到do
语句中的代码可能以两种方式失败,首先是网络调用失败然后,如果无法解析来自网络层的数据;再次,通过完成关闭,始终与顶层的通信完成。
最后,在这种情况下,ViewController可以使用解析层所期望的闭包调用下一层,并且能够处理源自任何层的错误:
override func viewDidLoad() {
super.viewDidLoad()
do {
try validator.validateInput("INPUT")
try ParsingLayer.requestObject("INPUT", completion: { (getObject) in
do {
let object = try getObject()
try self.validator.validateOutput(object)
print(object)
}
catch {
self.handleError(error)
}
})
catch {
handleError(error)
}
}
请注意,在完成闭包中有一个do catch
,这是必要的,因为调用是异步的,现在响应已遍历所有层,并且实际上已更改为更专业的类型甚至可以验证结果,而无需为验证功能创建图层。
希望它有所帮助。
答案 2 :(得分:2)
如果你从不抛出甚至试图抓住,为什么要声明你的方法抛出?您可以通过所有图层使用throwable声明抛出错误,甚至可以更改每个级别的throwable类型。
更新:没想到抛弃不在异步操作中工作。使用NSNotification是一条很好的路线,或者您可以查看RXSwift或类似的解决方案。我个人的建议是使用RxSwift。这可以让你摆脱目前正在进行的回调地狱。
答案 3 :(得分:2)
您在异步代码中正确识别了错误处理的问题。
使用同步函数似乎很容易 - 它只返回错误代码,或者有一个额外的错误参数,或者使用新的Swift throws
语法。这是一个同步函数:
func computeSome() throws -> Some
这是异步函数的可行函数签名:
func computeSomeAsync(completion: (Some?, NSError?) -> ())
异步函数返回Void
并且不抛出。如果失败,则使用错误参数集调用其完成函数。
但是,完成处理程序变得非常麻烦,尤其是在嵌套代码中。
解决方案是使用 Future :
func computeSomeAsync() -> Future<Some>
此函数是异步的,不会抛出 - 并返回 Future 。那么,未来是什么?
未来代表异步函数的最终结果。当您调用异步函数时,它会立即返回,并为结果获得占位符。这称为 future ,最终将由计算该值的基础后台任务完成。
当底层任务最终成功时,此future将包含函数的计算值。如果失败,它将包含错误。
根据实际实施和未来图书馆的API,您可以通过注册 continuations 来获得结果:
let future = computeSomeAsync()
future.onSuccess { value in
print("Value: \(value)")
}
future.onFailure { error in
print("Error: \(error)")
}
一开始可能看起来很奇怪,但你可以用期货来做很棒的事情:
fetchUser(id).flatMap { user in
fetchProfileImage(user.profileImageUrl).flatMap { image in
cacheImage(image)
}
}
.onFailure { error in
print("Something went wrong: \(error)")
}
上述语句是异步的 - 以及函数fetchUser
,fetchProfileImage
和cacheImage
。包括错误处理。