• Moya 在Swift开发中起着重要的网络交互作用,但是还有不如之处,比如网络不可用时,返回的 Responsenil,这时还得去解析相应的 Error
  • Codable 可以帮助我们快速的解析数据,但是一旦声明的属性类型与json中的不一致,将无法正常解析; 而且对于模型中自定义属性名的处理也十分繁琐

解决的方案有很多,不过我比较习惯使用 MoyaMapper ,不仅可以解决上述问题,还提供了多种模型转换数据互转多种数据类型任意存储的便捷方法。掌控Moya的网络请求、数据解析与缓存简直易如反掌。

MoyaMapper是基于Moya和SwiftyJSON封装的工具,以Moya的plugin的方式来实现间接解析,支持RxSwift

GitHub: MoyaMapper

? 详细的使用请查看手册 https://MoyaMapper.github.io

特点

  • 支持jsonModel 自动映射 与 自定义映射
  • 无视 json 中值的类型,Model 中属性声明的是什么类型,它就是什么类型
  • 支持 json字符串Model
  • 插件方式,全方位保障Moya.Response,拒绝各种网络问题导致 Responsenil,将各式各样的原因导致的数据加载失败进行统一处理,开发者只需要关注 Response
  • 可选 - 支持数据随意缓存( JSONNumberStringBoolMoya.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.responsenil,根据transformError是否为true 判断是否创建一个自定义的 response 并返回出去。

➡ 本来可以请求到的数据内容

➡ 现在关闭网络,再请求数据

  • 正常情况下,即不做任何不处理的时候, Responsenil

  • 经过 MoyaMapperPlugin 处理的后可得到转换后的 Response ,如图

这里将请求失败进行了统一处理,无论是服务器还是自身网络的问题,retStatus 都为 MMStatusCode.loadFail,但是 errorDescription 会保持原来的样子并赋值给 retMsg

  • retStatus 值会从枚举 MMStatusCode 中取 loadFail.rawValue ,即 700
  • 取 类型为 ModelableParameterTypetypestatusCodeKey 所指定的值 为键名,retMsg也同理

ps: 这个时候可以通过判断 retStatusresponse.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重启后也可以获取到数据
二、缓存网络请求

内部缓存过程:

  1. APP首次启动并进行网络请求,网络数据将缓存起来
  2. APP再次启动并进行网络请求时,会先返回缓存的数据,等请求成功后再返回网络数据
  3. 其它情况只会加载网络数据
  4. 每次成功请求到数据后,都会对缓存的数据进行更新
// 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的网络请求、数据解析与缓存相关推荐

  1. Swift 3 网络请求+数据解析

    登录请求解析 请求体: 账号密码: let param = ["password":"123456","username":"yi ...

  2. 网络请求数据解析时,判断数据是否为空

    //判断是否为空 +(BOOL)IsStringEmptyOrNull:(NSString *)str {     if (!str) {         // null object         ...

  3. 网络请求数据demo

    主类 package com.bwie.yuekaoa; import android.os.AsyncTask; import android.support.v7.app.AppCompatAct ...

  4. 『Android』网络请求数据与JSON解析

    『Android』网络请求数据与JSON解析

  5. Android网络之数据解析----SAX方式解析XML数据

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  6. Swift 网络请求数据与解析

    一: Swift 网络数据请求与处理最常用第三方 又有时间出来装天才了,还是在学swift,从中又发现一些问题,这两天上网找博客看问题弄的真的心都累.博客一篇写出来,好多就直接照抄,就没有实质性的把问 ...

  7. afn访问本地html,Swift利用AFN实现封装网络请求详解

    前言 相信大家都知道,我们一般在一个项目中,网络请求都封装成一个单例,以确保整个项目的网络请求 Session 是同一个. 单例模式定义:一个类有且仅有一个实例,并且自行实例化向整个系统提供,下面话不 ...

  8. Fragment标签页+OKHttp网络请求数据+MVP模式

    分包方式 需要的第三方依赖 Fragment 新建两个fragment MainActivity 主页面布局 设置Fragment+tablayout的适配器 Fragment标签页结束 OKHttp ...

  9. Retrofit网络请求数据的使用

    需要添加的依赖 compile 'com.squareup.retrofit2:retrofit:2.3.0' 需要添加网络权限 <uses-permission android:name=&q ...

最新文章

  1. 用c#实现通讯中自定义发送序列化数据,可一定程度上实现可编程发送的功能
  2. 苹果cms10的php.ini目录列表,使用苹果CMSV10常见问题整理官方版
  3. hdu 1861-游船出租
  4. 电脑测速软件_网速慢,怎么办,教你测速,教你解决方案
  5. 并查集+基础知识点详解
  6. 基友扫雷通关跟我炫耀!于是用Python自动扫雷程序十秒通关
  7. 利用gp自己做的生成缓冲区的代码
  8. python字典添加列表,将Python字典/列表插入到SQL数据库中最有效的方法是什么?...
  9. php 呼叫中心 源码,FreeSWITCH+Workerman+PHP 搭建呼叫中心
  10. 第三周 3.13-3.19
  11. Rails 4.1.0 安装笔记
  12. Centos 安装libreoffice 以及 word转pdf转html转epub转txt
  13. lvds输入悬空_MAX 10高速LVDS I/O用户指南
  14. word中mathtype的公式后面的光标位置
  15. excel分列---多页批量操作--vba宏实现
  16. [CF_GYM102900L]Traveling in the Grid World
  17. python添加win32com包,将ppt转换为图片
  18. 单片机遥控开关系统设计(结构原理、电路、程序)
  19. 利用QQ文件中转站给多个好友或群友传送文件
  20. Python学习 Day37 jQuery框架01

热门文章

  1. 骁龙778gplus和骁龙870差多少 哪个好
  2. SAP UI5 图片显示控件 Avatar 的使用方式介绍试读版
  3. Android安全测试用例(网络资源学习记录)
  4. 茶学领域如何用的上计算机,计算机视觉图像处理技术在茶学领域应用分析
  5. Dell G3-3579 安装 Ubuntu16.04 并安装显卡驱动
  6. 呼叫中心系统的功能有哪些?
  7. 创星c1语言设置,推荐款中学生用的老年机
  8. ubuntu shell命令大全
  9. SWA(随机权重平均)——一种全新的模型优化方法
  10. 读书笔记《学习之道》芭芭拉·奥克利 - 三 最后总结