Swift - 利用RxSwift进行错误处理

在使用RxSwift进行响应式编程时不可避免的会出现Error事件,那么如何更好的处理这些Error

基于RxSwift的信号源、订阅者以及Swift面向协议的特性,先制定两个协议:

  • ErrorEmittable负责发送以及存储错误类型
  • ErrorHandlable负责处理错误类型
protocol ErrorEmittable {
    
    var error: BehaviorRelay {get}
}

protocol ErrorHandlable {
    
    func handleError(error: Swift.Error)
}

扩展ErrorEmittable协议,新增绑定Handler的方法。

extension ErrorEmittable where Self: NSObject {
    
    func bindErrorHandler(handler: H) where H: NSObject, H: ErrorHandlable {
        error.filterNil()
            .subscribe(onNext: { [weak handler] in
                handler?.handleError(error: $0)
            }).disposed(by: rx.disposeBag)
    }
}

调用运行时方法,扩展协议给类关联对象。

private var errorKey: UInt8 = 0
extension ErrorEmittable where Self: NSObject {
    
    var error: BehaviorRelay {
        get {
            if let lookup = objc_getAssociatedObject(self, &errorKey) as? BehaviorRelay {
                return lookup
            } else {
                let error = BehaviorRelay(value: nil)
                objc_setAssociatedObject(self, &errorKey, error, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                return error
            }
        }
    }
}

由于获取、设置关联对象基本是模板方法,此处通过制定协议将其进行封装,同时使NSObject默认遵循该协议

protocol AssociatedObject {
    
}

extension AssociatedObject where Self: NSObject {
    
    func getAssociatedObjectIfNilSetDefaultValue(key: UnsafePointer, initialiser: () -> R) -> R {
        if let lookup = objc_getAssociatedObject(self, key) as? R {
            return lookup
        } else {
            let value = initialiser()
            objc_setAssociatedObject(self, key, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            return value
        }
    }
}

extension NSObject: AssociatedObject {}

ErrorEmittable进而可以优化为以下代码

private var errorKey: UInt8 = 0
extension ErrorEmittable where Self: NSObject {
    
    var error: BehaviorRelay {
        getAssociatedObjectIfNilSetDefaultValue(key: &errorKey, initialiser: {
            BehaviorRelay(value: nil)
        })
    }
}

在App开发中常见错误类型有RxErrorMoyaErrorSDKErrorsCustomError,总体划分为两类:自定义错误类型,第三方框架错误类型。

为了便于管理,我们需要将多种第三方框架中错误转换为自定义错误类型。

此处使用适配器模式,通过不同的适配器将对应的第三方框架错误类型转换为自定义错误类型,进行集中处理。

enum CustomError: Swift.Error {
    
}

protocol ErrorAdpater {
    
    associatedtype E
    
    func trans(error: E) -> CustomError?
}

struct RxErrorAdpater: ErrorAdpater {
    
    typealias E = RxError
    
    func trans(error: RxError) -> CustomError? {
                /// 需要根据业务转换
        return nil
    }
}

struct MoyaErrorAdpater: ErrorAdpater {
    
    typealias E = MoyaError
    
    func trans(error: MoyaError) -> CustomError? {
                /// 需要根据业务转换
        return nil
    }
}

ErrorAdpater协议中提供的方法将类型转换为可选型,在具体实现该方法时可以通过返回nil的方式去忽略第三方框架的某种错误类型。

通过适配器转换第三方框架错误类型后,ErrorHandlable 需要进行优化。

extension ErrorHandlable {
    
    func handleError(error: Swift.Error) {
        var adpated: CustomError?
        if let rxError = error as? RxError {
            adpated = RxErrorAdpater().trans(error: rxError)
        } else if let moyaError = error as? MoyaError {
            adpated = MoyaErrorAdpater().trans(error: moyaError)
        } else if let customError = error as? CustomError {
            adpated = customError
        }
        guard let error = adpated else { return }
        hanleCustomError(error: error)
    }
    
    private func hanleCustomError(error: CustomError) {
        /// 处理所有类型
    }
}

-handleError(error: Swift.Error) 中进行类型判定目前看来是不可避免的,后期在错误类型增加后会造成if 臃肿。

在项目中最常见的EmitterHandler对应关系就是ViewModelViewController。在部分逻辑简单不需要ViewModelViewController中,它可以同时具备这两种功能。


以目前的代码来看,Emitter在传递错误信息时需要调用-accept方法,如果在后期需要debug或者在传递时增加额外的操作则不是很方便。所以在ErrorEmittable中扩展一个传递错误的方法以供遵循该协议的类型调用。

extension ErrorEmittable {
    
    func emitError(error: Swift.Error) {
                self.error.accept(error)
        }
}

你可能感兴趣的:(ios,swift)