【发布时间】:2016-06-07 06:07:26
【问题描述】:
通过将ErrorType 添加到 Swift,现在可以以更简洁、更简洁的方式表达错误和失败事件。作为 iOS 开发人员,我们不再局限于 NSError 的旧方式,笨重且难以使用。
ErrorType 很棒有几个原因:
- 函数中的通用参数
- 任何枚举都可以遵循并实现它
- 与接球/投球模式配合得更好
然而,我最近遇到了一些问题,特别是一个问题,我很想看看其他人如何解决这个问题。
例如,您正在构建一个类似于 Facebook 的社交网络应用程序,并且您拥有 Group 模型。当用户第一次加载您的应用程序时,您需要做两/三件事:
- 从您的服务器获取相关组。
- 获取磁盘上已保存的组(Realm、CoreData 等)
- 使用刚刚获取的远程副本更新本地副本。
在整个过程中,您可以将错误类型分为两个不同的类别:PersistenceError 和 NetworkError,其中符合 ErrorType 的枚举可能如下所示
enum PersistenceError: ErrorType {
case CreateFailed([String: AnyObject)
case FetchFailed(NSPredicate?)
}
enum NetworkError: ErrorType {
case GetFailed(AnyObject.Type) // where AnyObject is your Group model class
}
您可以使用多种方式/设计模式来传递错误。最常见的当然是 try/catch。
func someFunc() throws {
throw .GetFailed(Group.self)
}
在这里,因为 throw 函数还不能指定它们抛出的错误类型,虽然我怀疑这会改变,但您可以轻松地抛出 NetworkError 或 PersistenceError。
当使用更通用或功能更强大的方法时会出现问题,例如 ReactiveCocoa 或 Result。
func fetchGroupsFromRemote() -> SignalProducer<[Group], NetworkError> {
// fetching code here
}
func fetchGroupsFromLocal() -> SignalProducer<[Group], PersistenceError> {
// fetch from local
}
然后包装这两个调用:
func fetch() -> SignalProducer<Group, ???> {
let remoteProducer = self.fetchGroupsFromRemote()
.flatMap(.Concat) ) { self.saveGroupsToLocal($0) // returns SignalProducer<[Group], PersistenceError> }
let localProducer = self.fetchGroupsFromLocal()
return SignalProducer(values: [localProducer, remoteProducer]).flatten(.Merge)
}
标记为??? 的位置出现什么错误类型?是NetworkError 还是PersistenceError?您不能使用ErrorType,因为它不能用作具体类型,如果您尝试将其用作通用约束<E: ErrorType>,编译器仍然会抱怨说它需要E 类型的参数列表.
所以问题就变成了,在 try/catch 中更是如此,而在函数方法中更是如此,如何维护错误层次结构,以便可以在不同的ErrorType 一致性中保留错误信息,同时仍然具有描述性错误 api。
到目前为止,我能想到的最好的是:
enum Error: ErrorType {
// Network Errors
case .GetFailed
// Persistence Errors
case .FetchFailed
// More error types
}
这本质上是一个长错误枚举,因此任何和所有错误都属于同一类型,即使是最深的错误也可以向上传播。
其他人如何处理这个问题?我享受拥有一个通用错误枚举的好处,但可读性和 api 澄清会受到影响。我宁愿让每个函数描述它们返回的特定错误集群,而不是让每个函数都返回 Error,但同样,我不知道如何在不丢失错误信息的情况下做到这一点。
【问题讨论】:
标签: ios swift error-handling