Swift 掌控Moya的网络请求、数据解析与缓存

栏目: Swift · 发布时间: 5年前

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

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

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

GitHub: MoyaMapper

:book: 详细的使用请查看手册MoyaMapper.github.io

特点

  • 支持 jsonModel 自动映射 与 自定义映射
  • 无视 json 中值的类型, Model 中属性声明的是什么类型,它就是什么类型
  • 支持 json字符串Model
  • 插件方式,全方位保障 Moya.Response ,拒绝各种网络问题导致 Responsenil ,将各式各样的原因导致的数据加载失败进行统一处理,开发者只需要关注 Response
  • 可选 - 支持数据随意缓存( JSONNumberStringBoolMoya.Response )
  • 可选 - 支持网络请求缓存

数据解析

一、插件注入

附: 插件 MoyaMapperPlugin 的详细使用

Swift 掌控Moya的网络请求、数据解析与缓存

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 是最合适不过了

Swift 掌控Moya的网络请求、数据解析与缓存
// 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)
复制代码

附: fetchJSONString的详细使用

Swift 掌控Moya的网络请求、数据解析与缓存
// 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)
复制代码

附: mapArray的详细使用说明

Swift 掌控Moya的网络请求、数据解析与缓存
// 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)
复制代码

附: 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 并返回出去。

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

Swift 掌控Moya的网络请求、数据解析与缓存

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

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

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

Swift 掌控Moya的网络请求、数据解析与缓存

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

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

ps: 这个时候可以通过判断 retStatusresponse.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重启后也可以获取到数据

二、缓存网络请求

内部缓存过程:

  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 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的网络请求、数据解析与缓存》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法笔记

算法笔记

胡凡、曾磊 / 机械工业出版社 / 2016-7 / 65

这是一本零基础就能读懂的算法书籍,读者不需要因为自己没有语言基础而畏惧。书籍的第2章便是一个C语言的入门教程,内容非常易懂,并且十分实用,阅读完这章就可以对本书需要的C语言基础有一个较好的掌握。 本书已经覆盖了大部分基础经典算法,不仅可以作为考研机试和PAT的学习教材,对其他的一些算法考试(例如CCF的CSP考试)或者考研初试的数据结构科目的学习和理解也很有帮助,甚至仅仅想学习经典算法的读者......一起来看看 《算法笔记》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换