在使用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开发中常见错误类型有RxError
、MoyaError
、SDKErrors
、CustomError
,总体划分为两类:自定义错误类型,第三方框架错误类型。
为了便于管理,我们需要将多种第三方框架中错误转换为自定义错误类型。
此处使用适配器模式,通过不同的适配器将对应的第三方框架错误类型转换为自定义错误类型,进行集中处理。
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
臃肿。
在项目中最常见的Emitter
、Handler
对应关系就是ViewModel
与ViewController
。在部分逻辑简单不需要ViewModel
的ViewController
中,它可以同时具备这两种功能。
以目前的代码来看,Emitter
在传递错误信息时需要调用-accept
方法,如果在后期需要debug
或者在传递时增加额外的操作则不是很方便。所以在ErrorEmittable
中扩展一个传递错误的方法以供遵循该协议的类型调用。
extension ErrorEmittable {
func emitError(error: Swift.Error) {
self.error.accept(error)
}
}