Swift - 在保持api清晰度的同时传播错误

时间:2016-06-07 06:07:26

标签: ios swift error-handling

通过向Swift添加ErrorType,现在可以更清晰,更简洁的方式表达错误和失败事件。我们不再将iOS开发人员绑定到NSError的旧方式,笨拙且难以使用。

ErrorType很有用,原因如下:

  • 功能中的通用参数
  • 任何枚举都可以符合并实施
  • 使用catch / throw范例更好地工作

然而,我最近遇到了一些问题,尤其是一个问题,我很想知道别人如何解决这个问题。

比如说,您构建了一个类似于Facebook的社交网络应用程序,并且您拥有Group模型。当用户首次加载您的应用程序时,您希望执行两件/三件事:

  1. 从您的服务器获取相关组。
  2. 获取磁盘上已存在的组(Realm,CoreData等)
  3. 使用刚刚提取的远程副本更新本地副本。
  4. 在整个过程中,您可以将错误类型分为两个不同的类别:PersistenceErrorNetworkError符合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)
    }
    

    这里,因为抛出的函数还不能指定它们抛出的错误类型,虽然我怀疑它会改变,但你可以轻松地抛出NetworkError或{{1} }。

    当使用更通用或功能性的方法时会出现问题,例如ReactiveCocoa或Result。

    PersistenceError

    然后包装两个电话:

    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>的参数列表。

    所以问题变得越来越少,使用try / catch以及功能方法更是如此,如何维护错误层次结构,以便在不同E一致性的同时保留错误信息,同时仍然具有描述性错误api

    到目前为止我能想到的最好的是:

    ErrorType

    基本上是一个长错误枚举,因此任何和所有错误都是相同的类型,甚至最深的错误也可以在链中传播。

    其他人如何处理此问题?我喜欢有一个通用错误枚举的好处,但可读性和api澄清受到影响。我更倾向于让每个函数描述他们返回的特定错误集群,而不是让每个函数返回enum Error: ErrorType { // Network Errors case .GetFailed // Persistence Errors case .FetchFailed // More error types } 但是,我再也看不到如何做到这一点而不会丢失错误信息。< / p>

1 个答案:

答案 0 :(得分:1)

只是尝试解决您的问题,可能不是一个完美的解决方案。使用协议轻松传递错误对象:

//1.
protocol Error {
  func errorDescription()
}

//2.
enum PersistenceError: ErrorType, Error {
  case CreateFailed([String: AnyObject)
  case FetchFailed(NSPredicate?)

  func errorDescription() {

  }
}

//3.
enum NetworkError: ErrorType, Error {
  case GetFailed(AnyObject.Type) // where AnyObject is your Group model class
  func errorDescription() {

  }
}

//5.
func fetch() -> SignalProducer<Group, Error> {
  ...
}