内容简介:解决的方案有很多,不过我比较习惯使用GitHub:
-
Moya
在Swift开发中起着重要的网络交互作用,但是还有不如之处,比如网络不可用时,返回的Response
为nil
,这时还得去解析相应的Error
-
Codable
可以帮助我们快速的解析数据,但是一旦声明的属性类型与json中的不一致,将无法正常解析; 而且对于模型中自定义属性名的处理也十分繁琐
解决的方案有很多,不过我比较习惯使用 MoyaMapper
,不仅可以解决上述问题,还提供了多种 模型转换
、 数据互转
、 多种数据类型任意存储
的便捷方法。掌控Moya的网络请求、数据解析与缓存简直易如反掌。
MoyaMapper
是基于Moya和SwiftyJSON封装的工具,以Moya的plugin的方式来实现间接解析,支持RxSwift
GitHub: MoyaMapper
:book: 详细的使用请查看手册MoyaMapper.github.io
特点
- 支持
json
转Model
自动映射 与 自定义映射 - 无视
json
中值的类型,Model
中属性声明的是什么类型,它就是什么类型 - 支持
json字符串
转Model
- 插件方式,全方位保障
Moya.Response
,拒绝各种网络问题导致Response
为nil
,将各式各样的原因导致的数据加载失败进行统一处理,开发者只需要关注Response
- 可选 - 支持数据随意缓存(
JSON
、Number
、String
、Bool
、Moya.Response
) - 可选 - 支持网络请求缓存
数据解析
一、插件注入
1、定义适用于项目接口的 ModelableParameterType
// statusCodeKey、tipStrKey、 modelKey 可以任意指定级别的路径,如: "error>used" struct NetParameter : ModelableParameterType { var successValue = "000" var statusCodeKey = "retStatus" var tipStrKey = "retMsg" var modelKey = "retBody" } 复制代码
2、在 MoyaProvider
中使用 MoyaMapperPlugin
插件,并指定 ModelableParameterType
let lxfNetTool = MoyaProvider<LXFNetworkTool>(plugins: [MoyaMapperPlugin(NetParameter())]) 复制代码
:exclamation: 使用 MoyaMapperPlugin
插件是整个 MoyaMapper
的核心所在!
二、Model声明
Model
需遵守 Modelable
协议
MoyaMapper Model
1、一般情况下如下写法即可
struct CompanyModel: Modelable { var name : String = "" var catchPhrase : String = "" init() { } } 复制代码
2、如果自定义映射,则可以实现方法 mutating func mapping(_ json: JSON)
struct CompanyModel: Modelable { var name : String = "" var catchPhrase : String = "" init() { } mutating func mapping(_ json: JSON) { self.name = json["nickname"].stringValue } } 复制代码
3、支持模型嵌套
struct UserModel: Modelable { var id : String = "" var name : String = "" var company : CompanyModel = CompanyModel() init() { } } 复制代码
三、Response 解析
1、以下示例皆使用了 MoyaMapperPlugin
,所以不需要指定 解析路径
2、如果没有使用 MoyaMapperPlugin
则需要指定 解析路径
,否则无法正常解析
ps: 解析路径
可以使用 a>b
这种形式来解决多级路径的问题
如果接口请求后 json
的数据结构与下图类似,则使用 MoyaMapper
是最合适不过了
// Normal let model = response.mapObject(MMModel.self) print("name -- \(model.name)") print("github -- \(model.github)") // 打印json print(response.fetchJSONString()) // Rx rxRequest.mapObject(MMModel.self) .subscribe(onSuccess: { (model) in print("name -- \(model.name)") print("github -- \(model.github)") }).disposed(by: disposeBag) 复制代码
// Normal let models = response.mapArray(MMModel.self) let name = models[0].name print("count -- \(models.count)") print("name -- \(name)") // 打印 json 模型数组中第一个的name print(response.fetchString(keys: [0, "name"])) // Rx rxRequest.mapArray(MMModel.self) .subscribe(onSuccess: { models in let name = models[0].name print("count -- \(models.count)") print("name -- \(name)") }).disposed(by: disposeBag) 复制代码
// Normal let (isSuccess, tipStr) = response.mapResult() print("isSuccess -- \(isSuccess)") print("tipStr -- \(tipStr)") // Rx rxRequest.mapResult() .subscribe(onSuccess: { (isSuccess, tipStr) in print("isSuccess -- \(isSuccess)") // 是否为 "000" print("retMsg -- \(retMsg)") // "缺少必要参数" }).disposed(by: disposeBag) 复制代码
统一处理网络请求结果
在APP的实际使用过程中,会遇到各种各样的网络请求结果,如:服务器挂了、手机无网络,此时 Moya
返回的 Response
为 nil,这样我们就不得不去判断 Error
。但是使用 MoyaMapperPlugin
就可以让我们只关注 Response
// MoyaMapperPlugin 的初始化方法 public init<T: ModelableParameterType>( _ type: T, transformError: Bool = true ) type : ModelableParameterType 用于定义字段路径,做为全局解析数据的依据 transformError : Bool 是否当网络请求失败时,自动转换请求结果,默认为 true 复制代码
- 当请求失败的时候,此时的
result.response
为nil
,根据transformError
是否为true
判断是否创建一个自定义的response
并返回出去。
➡ 本来可以请求到的数据内容
➡ 现在关闭网络,再请求数据
-
正常情况下,即不做任何不处理的时候,
Response
为nil
-
经过
MoyaMapperPlugin
处理的后可得到转换后的Response
,如图
这里将请求失败进行了统一处理,无论是服务器还是自身网络的问题, retStatus
都为 MMStatusCode.loadFail,但是 errorDescription 会保持原来的样子并赋值给 retMsg
。
-
retStatus
值会从枚举MMStatusCode
中取loadFail.rawValue
,即700
- 取 类型为
ModelableParameterType
的type
中statusCodeKey
所指定的值 为键名,retMsg
也同理
ps: 这个时候可以通过判断 retStatus
或 response.statusCode
是否与 MMStatusCode.loadFail.rawValue
相同来判断是否显示加载失败的空白页占位图
enum MMStatusCode: Int { case cache = 230 case loadFail = 700 } 复制代码
枚举 MMStatusCode
中除了 loadFail
,还有 cache
,我们已经知道 loadFail
在数据加载失败的时候会出现,那 cache
是在什么时候出来呢?不急,看下一节就知道了。
数据缓存
一、基本使用
// 缓存 @discardableResult MMCache.shared.cache`XXX`(value : XXX, key: String, cacheContainer: MMCache.CacheContainer = .RAM) -> Bool // 取舍 MMCache.shared.fetch`XXX`Cache(key: String, cacheContainer: MMCache.CacheContainer = .RAM) 复制代码
缓存成功会返回一个 Bool
值,这里可不接收
XXX 所支持类型 | |
---|---|
Bool | - |
Float | - |
Double | - |
String | - |
JSON | - |
Modelable | [Modelable] |
Moya.Response | - |
Int | UInt |
Int8 | UInt8 |
Int16 | UInt16 |
Int32 | UInt32 |
Int64 | UInt64 |
其中,除了 Moya.Response
之外,其它类型皆是通过 JSON
来实现缓存
所以,如果你想清除这些类型的缓存,只需要调用如下方法即可
@discardableResult func removeJSONCache(_ key: String, cacheContainer: MMCache.CacheContainer = .RAM) -> Bool @discardableResult func removeAllJSONCache(cacheContainer: MMCache.CacheContainer = .RAM) -> Bool 复制代码
清除 Moya.Response
则使用如下两个方法
@discardableResult func removeResponseCache(_ key: String) -> Bool @discardableResult func removeAllResponseCache() -> Bool 复制代码
再来看看MMCache.CacheContainer
enum CacheContainer { case RAM // 只缓存于内存的容器 case hybrid // 缓存于内存与磁盘的容器 } 复制代码
这两种容器互不相通,即 即使key相同,使用 hybrid
来缓存后,再通过 RAM
取值是取不到的。
- RAM : 仅缓存于内存之中,缓存的数据在APP使用期间一直存在
- hybrid :缓存于内存与磁盘中,APP重启后也可以获取到数据
二、缓存网络请求
内部缓存过程:
- APP首次启动并进行网络请求,网络数据将缓存起来
- APP再次启动并进行网络请求时,会先返回缓存的数据,等请求成功后再返回网络数据
- 其它情况只会加载网络数据
- 每次成功请求到数据后,都会对缓存的数据进行更新
// Normal func cacheRequest( _ target: Target, cacheType: MMCache.CacheKeyType = .default, callbackQueue: DispatchQueue? = nil, progress: Moya.ProgressBlock? = nil, completion: @escaping Moya.Completion ) -> Cancellable // Rx func cacheRequest( _ target: Base.Target, callbackQueue: DispatchQueue? = nil, cacheType: MMCache.CacheKeyType = .default ) -> Observable<Response> 复制代码
实际上是对 Moya
请求后的 Response
进行缓存。 其实与 Moya
自带的方法相比较只多了一个参数 cacheType: MMCache.CacheKeyType
,定义着缓存中的 key
,默认为 default
下面是 MMCache.CacheKeyType
的定义
/** let cacheKey = [method]baseURL/path - default : cacheKey + "?" + parameters - base : cacheKey - custom : cacheKey + "?" + customKey */ public enum CacheKeyType { case `default` case base case custom(String) } 复制代码
如果你想缓存 多页
列表数据的 最新一页
数据,此时使用 default
是不合适的,因为 default
使用的 key
包含了 pageIndex
,这样就达不到只缓存 最新一页数据
的目的, 所以这里应该使用 base
或者 custom(String)
我们可以来试一下带缓存的请求
/* * APP第一次启动并进行网络请求,网络数据将缓存起来 * APP再次启动并进行网络请求时,会先加载缓存,再加载网络数据 * 其它情况只会加载网络数据 * 每次成功请求到数据都会进行数据更新 */ lxfNetTool.rx.cacheRequest(.data(type: .all, size: 10, index: 1)) .subscribe(onNext: { response in log.debug("statusCode -- \(response.statusCode)") }).disposed(by: disposeBag) // 传统方式 /* let _ = lxfNetTool.cacheRequest(.data(type: .all, size: 10, index: 1)) { result in guard let resp = result.value else { return } log.debug("statusCode -- \(resp.statusCode)") } */ 复制代码
打印结果
// 首次使用APP statusCode -- 200 // 关闭并重新打开APP,再请求一下 statusCode -- 230 statusCode -- 200 // 然后再请求一下 statusCode -- 200 复制代码
这里的 230
就是 MMStatusCode.cache.rawValue
以上所述就是小编给大家介绍的《Swift 掌控Moya的网络请求、数据解析与缓存》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 全面认识 RUST -- 掌控未来的雷电
- 对话“机械战甲”之父 脑机交互掌控未来
- 如何写一篇掌控面试节奏的简历?
- BetterHash协议:让矿工重新掌控自己的哈希算力
- 为了追求极致的性能,Kafka掌控这11项要领
- 助你掌控微服务架构的20个Go语言项目
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
python 3标准库
道格·赫尔曼 / 机械工业出版社 / 2018-10 / 199
在本书中,你会看到用来处理文本、数据类型、算法、数学计算、文件系统、网络通信、Internet、XML、Email、加密、并发性、运行时和语言服务等各个方面的实用代码和解决方案。在内容安排上,每一节都会全面介绍一个模块,并提供一些很有价值的补充资源链接,这使得本书成为一本理想的Python标准库参考手册。一起来看看 《python 3标准库》 这本书的介绍吧!