Swift 掌控Moya的网络请求、数据解析与缓存
Moya
在Swift开发中起着重要的网络交互作用,但是还有不如之处,比如网络不可用时,返回的Response
为nil
,这时还得去解析相应的Error
Codable
可以帮助我们快速的解析数据,但是一旦声明的属性类型与json中的不一致,将无法正常解析; 而且对于模型中自定义属性名的处理也十分繁琐
解决的方案有很多,不过我比较习惯使用 MoyaMapper
,不仅可以解决上述问题,还提供了多种模型转换
、数据互转
、多种数据类型任意存储
的便捷方法。掌控Moya的网络请求、数据解析与缓存简直易如反掌。
MoyaMapper
是基于Moya和SwiftyJSON封装的工具,以Moya的plugin的方式来实现间接解析,支持RxSwiftGitHub: MoyaMapper
? 详细的使用请查看手册 https://MoyaMapper.github.io
特点
- 支持
json
转Model
自动映射 与 自定义映射 - 无视
json
中值的类型,Model
中属性声明的是什么类型,它就是什么类型 - 支持
json字符串
转Model
- 插件方式,全方位保障
Moya.Response
,拒绝各种网络问题导致Response
为nil
,将各式各样的原因导致的数据加载失败进行统一处理,开发者只需要关注Response
- 可选 - 支持数据随意缓存(
JSON
、Number
、String
、Bool
、Moya.Response
) - 可选 - 支持网络请求缓存
数据解析
一、插件注入
附:插件 MoyaMapperPlugin 的详细使用
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())])
❗ 使用 MoyaMapperPlugin
插件是整个 MoyaMapper
的核心所在!
二、Model声明
Model
需遵守Modelable
协议
MoyaMapper
支持模型自动映射 和 自定义映射- 不需要考虑源json数据的真实类型,这里统一按
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
这种形式来解决多级路径的问题
解析方法如下列表所示
方法 | 描述 (支持RxSwift) |
---|---|
toJSON | Response 转 JSON ( toJSON | rx.toJSON) |
fetchString | 获取指定路径的字符串( fetchString | rx.fetchString) |
fetchJSONString | 获取指定路径的原始json字符串 ( fetchJSONString | rx.fetchJSONString ) |
mapResult |
Response -> MoyaMapperResult (Bool, String) ( mapResult | rx.mapResult )
|
mapObject | Response -> Model ( mapObject | rx.mapObject) |
mapObjResult | Response -> (MoyaMapperResult, Model) ( mapObjResult | rx.mapObjResult) |
mapArray | Response -> [Model]( mapArray | rx.mapArray) |
mapArrayResult | Response -> (MoyaMapperResult, [Model]) ( mapArrayResult | rx.mapArrayResult) |
❗除了 fetchJSONString
的默认解析路径是根路径
之外,其它方法的默认解析路径为插件对象中的 modelKey
如果接口请求后 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) inprint("name -- \(model.name)")print("github -- \(model.github)")}).disposed(by: disposeBag)
附: fetchJSONString的详细使用
// 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 inlet name = models[0].nameprint("count -- \(models.count)")print("name -- \(name)")}).disposed(by: disposeBag)
附:mapArray的详细使用说明
// Normal
let (isSuccess, tipStr) = response.mapResult()
print("isSuccess -- \(isSuccess)")
print("tipStr -- \(tipStr)")// Rx
rxRequest.mapResult().subscribe(onSuccess: { (isSuccess, tipStr) inprint("isSuccess -- \(isSuccess)") // 是否为 "000"print("retMsg -- \(retMsg)") // "缺少必要参数"}).disposed(by: disposeBag)
附:mapResult的详细使用说明
统一处理网络请求结果
在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 = 230case 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 basecase custom(String)
}
如果你想缓存
多页
列表数据的最新一页
数据,此时使用default
是不合适的,因为default
使用的key
包含了pageIndex
,这样就达不到只缓存最新一页数据
的目的, 所以这里应该使用base
或者custom(String)
我们可以来试一下带缓存的请求
/** APP第一次启动并进行网络请求,网络数据将缓存起来* APP再次启动并进行网络请求时,会先加载缓存,再加载网络数据* 其它情况只会加载网络数据* 每次成功请求到数据都会进行数据更新*/
lxfNetTool.rx.cacheRequest(.data(type: .all, size: 10, index: 1)).subscribe(onNext: { response inlog.debug("statusCode -- \(response.statusCode)")}).disposed(by: disposeBag)// 传统方式
/*
let _ = lxfNetTool.cacheRequest(.data(type: .all, size: 10, index: 1)) { result inguard 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
CocoaPods
- 默认安装
MoyaMapper默认只安装Core下的文件
pod 'MoyaMapper'
- RxSwift拓展
pod 'MoyaMapper/Rx'
- 缓存拓展
pod 'MoyaMapper/MMCache'
- Rx缓存
pod 'MoyaMapper/RxCache'
Swift 掌控Moya的网络请求、数据解析与缓存相关推荐
- Swift 3 网络请求+数据解析
登录请求解析 请求体: 账号密码: let param = ["password":"123456","username":"yi ...
- 网络请求数据解析时,判断数据是否为空
//判断是否为空 +(BOOL)IsStringEmptyOrNull:(NSString *)str { if (!str) { // null object ...
- 网络请求数据demo
主类 package com.bwie.yuekaoa; import android.os.AsyncTask; import android.support.v7.app.AppCompatAct ...
- 『Android』网络请求数据与JSON解析
『Android』网络请求数据与JSON解析
- Android网络之数据解析----SAX方式解析XML数据
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...
- Swift 网络请求数据与解析
一: Swift 网络数据请求与处理最常用第三方 又有时间出来装天才了,还是在学swift,从中又发现一些问题,这两天上网找博客看问题弄的真的心都累.博客一篇写出来,好多就直接照抄,就没有实质性的把问 ...
- afn访问本地html,Swift利用AFN实现封装网络请求详解
前言 相信大家都知道,我们一般在一个项目中,网络请求都封装成一个单例,以确保整个项目的网络请求 Session 是同一个. 单例模式定义:一个类有且仅有一个实例,并且自行实例化向整个系统提供,下面话不 ...
- Fragment标签页+OKHttp网络请求数据+MVP模式
分包方式 需要的第三方依赖 Fragment 新建两个fragment MainActivity 主页面布局 设置Fragment+tablayout的适配器 Fragment标签页结束 OKHttp ...
- Retrofit网络请求数据的使用
需要添加的依赖 compile 'com.squareup.retrofit2:retrofit:2.3.0' 需要添加网络权限 <uses-permission android:name=&q ...
最新文章
- 用c#实现通讯中自定义发送序列化数据,可一定程度上实现可编程发送的功能
- 苹果cms10的php.ini目录列表,使用苹果CMSV10常见问题整理官方版
- hdu 1861-游船出租
- 电脑测速软件_网速慢,怎么办,教你测速,教你解决方案
- 并查集+基础知识点详解
- 基友扫雷通关跟我炫耀!于是用Python自动扫雷程序十秒通关
- 利用gp自己做的生成缓冲区的代码
- python字典添加列表,将Python字典/列表插入到SQL数据库中最有效的方法是什么?...
- php 呼叫中心 源码,FreeSWITCH+Workerman+PHP 搭建呼叫中心
- 第三周 3.13-3.19
- Rails 4.1.0 安装笔记
- Centos 安装libreoffice 以及 word转pdf转html转epub转txt
- lvds输入悬空_MAX 10高速LVDS I/O用户指南
- word中mathtype的公式后面的光标位置
- excel分列---多页批量操作--vba宏实现
- [CF_GYM102900L]Traveling in the Grid World
- python添加win32com包,将ppt转换为图片
- 单片机遥控开关系统设计(结构原理、电路、程序)
- 利用QQ文件中转站给多个好友或群友传送文件
- Python学习 Day37 jQuery框架01