内容简介:解决的方案有很多,不过我比较习惯使用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语言项目
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Think Python
Allen B. Downey / O'Reilly Media / 2012-8-23 / GBP 29.99
Think Python is an introduction to Python programming for students with no programming experience. It starts with the most basic concepts of programming, and is carefully designed to define all terms ......一起来看看 《Think Python》 这本书的介绍吧!
随机密码生成器
多种字符组合密码
RGB CMYK 转换工具
RGB CMYK 互转工具