【问题标题】:Swift - Error propagation while maintaining api claritySwift - 错误传播,同时保持 api 清晰
【发布时间】:2016-06-07 06:07:26
【问题描述】:

通过将ErrorType 添加到 Swift,现在可以以更简洁、更简洁的方式表达错误和失败事件。作为 iOS 开发人员,我们不再局限于 NSError 的旧方式,笨重且难以使用。

ErrorType 很棒有几个原因:

  • 函数中的通用参数
  • 任何枚举都可以遵循并实现它
  • 与接球/投球模式配合得更好

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

例如,您正在构建一个类似于 Facebook 的社交网络应用程序,并且您拥有 Group 模型。当用户第一次加载您的应用程序时,您需要做两/三件事:

  1. 从您的服务器获取相关组。
  2. 获取磁盘上已保存的组(Realm、CoreData 等)
  3. 使用刚刚获取的远程副本更新本地副本。

在整个过程中,您可以将错误类型分为两个不同的类别: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)
}

在这里,因为 throw 函数还不能指定它们抛出的错误类型,虽然我怀疑这会改变,但您可以轻松地抛出 NetworkErrorPersistenceError

当使用更通用或功能更强大的方法时会出现问题,例如 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,因为它不能用作具体类型,如果您尝试将其用作通用约束&lt;E: ErrorType&gt;,编译器仍然会抱怨说它需要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


    【解决方案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> {
      ...
    }
    

    【讨论】:

    • 这当然是一个很好的解决方案。不幸的是,它仍然存在同样的问题,即任何返回错误类型的函数或方法都将始终是 Error 类型。
    • 对,但即使类型是错误,它实际上是具有基类指针的子类对象。并且您可以根据协议方法的子类类型实现获得动态行为。可能是我没有准确了解上下文,但您也可以通过向 ErrorType 添加扩展方法来改进行为,并将其与任何自定义子类类型无缝使用。
    猜你喜欢
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 2011-05-25
    • 1970-01-01
    • 2020-02-19
    • 1970-01-01
    • 1970-01-01
    • 2014-04-05
    相关资源
    最近更新 更多