内容简介:大多数 APP 都需要向服务器请求数据,一般来说,一个 APP 只需要根据一个后台设计一套网络请求的封装即可。但是在开发工作中,可能一个 APP 需要接入其他产线的功能,甚至有可能同一个后台返回的接口也不能适用同一个解析规则。当出现这种情况时,当我们使用这些工具时,往往需要有一个确定的类型,才能完成 data 到 model 的映射。在这个阶段,一般是这样来设计模型:这样来设计 Network:
大多数 APP 都需要向服务器请求数据,一般来说,一个 APP 只需要根据一个后台设计一套网络请求的封装即可。但是在开发工作中,可能一个 APP 需要接入其他产线的功能,甚至有可能同一个后台返回的接口也不能适用同一个解析规则。当出现这种情况时, MJExtension
、 ObjectMapper
、 HandyJSON
等模型转换的 工具 应运而生。
模型转换
当我们使用这些工具时,往往需要有一个确定的类型,才能完成 data 到 model 的映射。在这个阶段,一般是这样来设计模型:
class BaseRespose { var code: Int? var msg: String? } class UserInfo { var name: String? var age: Int? } class UserInfoResponse: BaseRespose { var data: UserInfo? } 复制代码
这样来设计 Network:
network<T: BaseResponse>(api: String, success((data: T) -> ())) 复制代码
在这个阶段,我们运用泛型约束了模型类。使得任何继承了 BaseResponse
或实现了 BaseResponse
协议的类或结构体可以成功的解析。这样看来,似乎已经可以做到解析所有的数据结构了,但需要注意的是,此时的 Network只能处理 BaseRespose
,也就意味着这时的 Network 只能处理一种类型。
举例来说,当加入新的接口,且 code
或 msg
的解析规则发生变化时,现在的 Network 就无法使用。
当然,在这个例子中,办法还是有的,比如:
class BaseRespose {} class UserInfo { var name: String? var age: Int? } class UserInfoResponse: BaseRespose { var code: Int? var msg: String? var data: UserInfo? } 复制代码
BaseRespose
不处理任何解析实现,依靠确定的类型 UserInfoResponse
进行解析,但这样你会发现,无法从 Network 内部获取 code
从而判断请求状态。进行统一的处理,其次,也会产生冗余代码。
而这种情况下,只能是增加 Network 的请求方法,来适应两种不同的结构。
同时,除了增加请求方法之外,你无法使其返回 data、string、json 等数据类型。
其次,在依靠继承关系组成模型的情况下,你也无法使用结构体来进行模型的声明。
因此,一个组件化的 Network,为了适应不同的后台或不同的数据结构,应该具备可以解析任意传入的类型,并进行输出,同时可以在 Network 的内部对请求结果进行统一的处理。且应该支持类与结构体。
下面我给大家介绍一个网络请求组件,在这个组件中,依赖了以下几个优秀的开源工具,其具体使用不再细表:
## 网络请求 s.dependency 'Moya', '~> 11.0' ## 模型解析 s.dependency 'ObjectMapper', '~> 3.3' ## 响应式 s.dependency 'RxSwift', '~> 4.0' s.dependency 'RxCocoa', '~> 4.0' ## JSON数据处理 s.dependency 'SwiftyJSON', '~> 4.0' 复制代码
如何针对不同后台进行设置
JingDataNetworkConfig
顾名思义。是全局的配置项。
其中 plugins
是 Moya
的插件机制,可以实现 log、缓存等功能。
static func handleJingDataNetworkError(_ error: JingDataNetworkError)
则是处理全局请求异常的地方。
当声明一个 Network 时,需要传入此协议的实现。
public protocol JingDataNetworkConfig { static var networkManager: Manager { set get } static var plugins: [PluginType] { set get } static func handleJingDataNetworkError(_ error: JingDataNetworkError) } 复制代码
如何实现接收任意模型(解析规则)
首先需要定义 BaseResponse
协议,协议继承了 Mappable
, Mappable
是 ObjectMapper
中的一项协议,实现了 ObjectMapper
协议的类或结构体,才可以进行解析。
associatedtype DataSource
表示此协议关联了一个泛型。它的作用稍后会讲到。
func makeCustomJingDataError() -> JingDataNetworkError?
是用于制作模型解析成功后的状态错误。
public protocol JingDataNetworkBaseResponse: Mappable { associatedtype DataSource func makeCustomJingDataError() -> JingDataNetworkError? } 复制代码
如何实现解析任意的模型
在此方法中,利用泛型约束了一个 JingDataNetworkBaseResponse
类型,并规范返回的结果也是此类型,然后将这个类型传递给 JingDataNetworkDataParser
。
func createObserver<R: JingDataNetworkBaseResponse>(api: T, test: Bool = false, progress: ProgressBlock? = nil) -> Observable<R> { return createGeneralObserver(api: api, test: test, progress: progress, success: { (ob, resp) in do { let model: R = try JingDataNetworkDataParser.handle(data: resp.data) ob.onNext(model) } catch let error as JingDataNetworkError { self.handle(ob: ob, error: error) } catch {} }) } 复制代码
在 JingDataNetworkDataParser
方法中同样传入泛型,并将这个类型传递给 JingDataNetworkResponseBuilder
。在这个过程中 response.makeCustomJingDataError()
方法可以抛出一个错误交给全局和回调处理。
public static func handle<R: JingDataNetworkBaseResponse>(data: Data) throws -> R { guard let JSONString = String.init(data: data, encoding: .utf8) else { throw JingDataNetworkError.parser(.string) } guard let response: R = JingDataNetworkResponseBuilder.create(by: JSONString) else { throw JingDataNetworkError.parser(.model) } if let customError = response.makeCustomJingDataError() { throw customError } return response 复制代码
最终由下面的方法,利用 ObjectMapper
进行解析。
public static func create<R: Mappable>(by JSONString: String) -> R? { let mapperModel = Mapper<R>() let object = mapperModel.map(JSONString: JSONString) return object } 复制代码
如何实现解析任意的类型
对泛型不进行约束,判断泛型的类型尝试解析。如无法解析则进入全局错误处理和错误回调。
func createObserver<R>(api: T, test: Bool = false, progress: ProgressBlock? = nil) -> Observable<R> { if R.Type.self == String.Type.self { return createGeneralObserver(api: api, test: test, progress: progress, success: { (ob, resp) in do { let string: String = try JingDataNetworkDataParser.handle(resp: resp) ob.onNext(string as! R) } catch let error as JingDataNetworkError { self.handle(ob: ob, error: error) } catch {} }) } else if R.Type.self == Data.Type.self { return createGeneralObserver(api: api, test: test, progress: progress, success: { (ob, resp) in ob.onNext(resp.data as! R) }) } else if R.Type.self == UIImage.Type.self { return createGeneralObserver(api: api, test: test, progress: progress, success: { (ob, resp) in do { let image: UIImage = try JingDataNetworkDataParser.handle(resp: resp) ob.onNext(image as! R) } catch { self.handle(ob: ob, error: .parser(.image)) } }) } else if R.Type.self == JSON.Type.self { return createGeneralObserver(api: api, test: test, progress: progress, success: { (ob, resp) in do { let json: JSON = try JingDataNetworkDataParser.handle(data: resp.data) ob.onNext(json as! R) } catch let error as JingDataNetworkError { self.handle(ob: ob, error: error) } catch {} }) } else { return createGeneralObserver(api: api, test: test, progress: progress, success: { (ob, data) in self.handle(ob: ob, error: .parser(.type)) }) } } 复制代码
使用示例
实现一个后台对应的配置项:
struct BaseNetworkConfig: JingDataNetworkConfig { static var plugins: [PluginType] = [] static var networkManager: Manager { set { } get { let configuration = URLSessionConfiguration.default configuration.httpAdditionalHeaders = Manager.defaultHTTPHeaders configuration.timeoutIntervalForRequest = 15 configuration.timeoutIntervalForResource = 60 let manager = Manager(configuration: configuration) manager.startRequestsImmediately = false return manager } } static func handleJingDataNetworkError(_ error: JingDataNetworkError) { print(error.description) } } 复制代码
实现一个解析规则和状态错误抛出:
class BaseResp<T: Mappable>: JingDataNetworkBaseResponse { typealias DataSource = T var data: T? var code: Int? func makeCustomJingDataError() -> JingDataNetworkError? { guard let c = code else { return nil } guard c != 0 else { return nil } return JingDataNetworkError.custom(code: c) } } 复制代码
实现一个具体要解析的模型:
struct UserInfo: Mappable { var age: Int? var name: String? } 复制代码
发起一个模型解析网络请求:
JingDataNetworkManager<TestApi, BaseNetworkConfig> .base(api: .n) .observer(test: true, progress: { (data) in print(data.progress) }) .observeOn(MainScheduler.instance) .subscribe(onNext: { (data: BaseResp<UserInfo>) in print(data) }, onError: { (e) in print(e as! JingDataNetworkError) }) .disposed(by: bag) 复制代码
发起一个 String 解析网络请求:
JingDataNetworkManager<TestApi, BaseNetworkConfig> .base(api: .n) .observer(test: true, progress: { (data) in print(data.progress) }) .observeOn(MainScheduler.instance) .subscribe(onNext: { (data: String) in print(data) }, onError: { (e) in print(e as! JingDataNetworkError) }) .disposed(by: bag) 复制代码
发起一个 JSON 数组的网络请求:
JingDataNetworkManager<TestApi, BaseNetworkConfig> .base(api: .n) .observer(test: true, progress: { (data) in print(data.progress) }) .observeOn(MainScheduler.instance) .subscribe(onNext: { (data: JSON) in print(data.arrayValue.count) }, onError: { (e) in print(e as! JingDataNetworkError) }) .disposed(by: bag) 复制代码
至此,我们就可以针对一个后台的不同解析规则进行适配。并可以进一步进行封装,使其使用更加简洁。
其他问题
现在我们已经可以解决上面抛出的问题。但是还有两点做的不够好,一个是在模型的解析上只能依赖 ObjectMapper
进行处理,另一方面在数组类型方面也没有特别支持。
时序管理
除去模型的解析之外,在 Network 的工作中,请求顺序的管理也是一个重头戏。其请求的顺序一般有几种情况。
-
请求结果以相同模型解析
- 请求回调依次响应
- 全部请求完毕进行回调
-
请求结果以不同模型解析
- 请求回调依次响应
- 全部请求完毕进行回调
下面依次来看如何进行实现。
相同模型
public func zip<R>(apis: [T], progress: ProgressBlock? = nil, test: Bool = false) -> Observable<[R]> { var obs = [Observable<R>]() for api in apis { let ob: Observable<R> = JingDataNetworkManager<T, C>.base(api: api).observer(test: test, progress: progress) obs.append(ob) } return Observable.zip(obs) } public func map<R>(apis: [T], progress: ProgressBlock? = nil, test: Bool = false) -> Observable<R> { var obs = [Observable<R>]() for api in apis { let ob: Observable<R> = JingDataNetworkManager<T, C>.base(api: api).observer(test: test, progress: progress) obs.append(ob) } return Observable.from(obs).merge() } 复制代码
这里使用了 RxSwift
对请求结果分别进行打包和顺序处理。
使用示例:
JingDataNetworkSequencer<BaseNetworkConfig>.sameModel() .zip(apis: [TestApi.m, .n], test: true) .subscribe(onNext: { (d: [BaseResp<UserInfo>]) in print(d.map { $0.data!.name! }) }).disposed(by: bag) 复制代码
JingDataNetworkSequencer<BaseNetworkConfig>.sameModel() .map(apis: [TestApi.m, .n], test: true) .observeOn(MainScheduler.instance) .subscribe(onNext: { (d: BaseResp<UserInfo>) in print(d.data!.name!) }).disposed(by: bag) 复制代码
不同模型顺序请求
不同的模型相对复杂,因为它意味着不同的后台或解析规则,同时,顺序请求时,又要求可以获取上一次请求的结果,顺序请求完成时,又可以取得最终的请求结果。
在下面的实现中:
blocks
保存每次请求的代码块,如请求失败时则会打断下一次请求。
semaphore
是信号量,保证本次 block
完成前,下一个 block
会被阻塞。
data
是本次请求的结果,用于传给下一个请求。
public class JingDataNetworkDifferentModelSequencer<C: JingDataNetworkConfig> { var blocks = [JingDataNetworkViodCallback]() let semaphore = DispatchSemaphore(value: 1) var data: Any? var bag = DisposeBag() var requestSuccess = true var results = [Any]() var index: Int = 0 public func next<T: TargetType, N: JingDataNetworkBaseResponse, P>(with: @escaping (P) -> T?, progress: ProgressBlock? = nil, success: @escaping (N) -> (), error: ((Error) -> ())? = nil, test: Bool = false) -> JingDataNetworkDifferentModelSequencer { let api: () -> T? = { guard let preData = self.data as? P else { return nil } return with(preData) } return next(api: api, progress: progress, success: success, error: error, test: test) } public func next<T: TargetType, N: JingDataNetworkBaseResponse>(api: @escaping () -> T?, progress: ProgressBlock? = nil, success: @escaping (N) -> (), error: ((Error) -> ())? = nil, test: Bool = false) -> JingDataNetworkDifferentModelSequencer { let block: JingDataNetworkViodCallback = { guard let api = api() else { self.requestSuccess = false return } self.semaphore.wait() JingDataNetworkManager<T, C>.base(api: api).observer(test: test, progress: progress) .observeOn(MainScheduler.instance) .subscribe(onNext: { [weak self] (data: N) in self?.data = data self?.results.append(data) self?.requestSuccess = true success(data) self?.semaphore.signal() }, onError: { [weak self] (e) in self?.requestSuccess = false error?(e) self?.semaphore.signal() }) .disposed(by: self.bag) self.semaphore.wait() // print("xxxxxxxxx") self.semaphore.signal() } blocks.append(block) return self } public func run() -> PrimitiveSequence<SingleTrait, [Any]> { let ob = Single<[Any]>.create { (single) -> Disposable in let queue = DispatchQueue(label: "\(JingDataNetworkDifferentModelSequencer.self)", qos: .default, attributes: .concurrent) queue.async { for i in 0 ..< self.blocks.count { self.index = i guard self.requestSuccess else { break } self.blocks[i]() } if self.requestSuccess { single(.success(self.results)) } else { C.handleJingDataNetworkError(.sequence(.break(index: self.index))) single(.error(JingDataNetworkError.sequence(.break(index: self.index)))) } self.requestFinish() } return Disposables.create() } return ob } func requestFinish() { requestSuccess = true index = 0 blocks.removeAll() results.removeAll() } } 复制代码
示例:
JingDataNetworkSequencer<BaseNetworkConfig>.differentModel() .next(api: { () -> TestApi? in return .m }, success: { (data: String) in print(data) }, error: { (error) in print(error) }, test: true) .next(with: { (data: String) -> TestApi in return .m }, success: { (data: UserInfo) in print(data.data!.age!) }, error: { (error) in print(error) }, test: true) .run() .observeOn(MainScheduler.instance) .subscribe(onSuccess: { (result) in print("success", Thread.current) }) { (error) in print(error) }.disposed(by: bag) 复制代码
不同模型打包请求
public extension JingDataNetworkDifferentModelSequencer { public func observerOfzip<T: TargetType, R: JingDataNetworkBaseResponse>(api: T, progress: ProgressBlock? = nil, test: Bool = false) -> Observable<R> { return JingDataNetworkManager<T, C>.base(api: api).observer(test: test, progress: progress) } public func observerOfzip<T: TargetType, R>(api: T, progress: ProgressBlock? = nil, test: Bool = false) -> Observable<R> { return JingDataNetworkManager<T, C>.base(api: api).observer(test: test, progress: progress) } } 复制代码
不同模型的打包请求利用 RxSwift
很容易处理,示例如下:
let o1: Observable<BaseResp<UserInfo>> = JingDataNetworkSequencer<BaseNetworkConfig>.differentModel().observerOfzip(api: TestApi.m, test: true) let o2: Observable<BaseResp<UserInfo>> = JingDataNetworkSequencer<BaseNetworkConfig>.differentModel().observerOfzip(api: TestApi.n, test: true) let o3: Observable<String> = JingDataNetworkSequencer<BaseNetworkConfig>.differentModel().observerOfzip(api: Test2Api.n, test: true) let o4: Observable<BaseResp<UserInfo>> = JingDataNetworkSequencer<BaseNetworkConfig>.differentModel().observerOfzip(api: Test2Api.n, test: true) Observable.zip(o1, o2, o3, o4) .observeOn(MainScheduler.instance) .subscribe(onNext: { (str1, user1, str2, user2) in print(Thread.current) print(str1, user1, str2, user2) }, onError: { (e) in print(e) }) .disposed(by: bag) 复制代码
以上所述就是小编给大家介绍的《谈谈如何设计一个 Network->Model 组件》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 谈谈CocoaPods组件二进制化方案
- 谈谈CocoaPods组件二进制化方案
- 谈谈如何设计一个 Network->Model 组件
- 谈谈我理解的Android组件化
- 一个播放器引发的思考——谈谈React跨组件通信
- ????谈谈单元测试
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Principles of Object-Oriented JavaScript
Nicholas C. Zakas / No Starch Press / 2014-2 / USD 24.95
If you've used a more traditional object-oriented language, such as C++ or Java, JavaScript probably doesn't seem object-oriented at all. It has no concept of classes, and you don't even need to defin......一起来看看 《Principles of Object-Oriented JavaScript》 这本书的介绍吧!
JS 压缩/解压工具
在线压缩/解压 JS 代码
RGB HSV 转换
RGB HSV 互转工具