Moya
在Swift開發中起着重要的網絡交互做用,可是還有不如之處,好比網絡不可用時,返回的 Response
爲 nil
,這時還得去解析相應的 Error
Codable
能夠幫助咱們快速的解析數據,可是一旦聲明的屬性類型與json中的不一致,將沒法正常解析; 並且對於模型中自定義屬性名的處理也十分繁瑣解決的方案有不少,不過我比較習慣使用 MoyaMapper
,不只能夠解決上述問題,還提供了多種模型轉換
、數據互轉
、多種數據類型任意存儲
的便捷方法。掌控Moya的網絡請求、數據解析與緩存簡直易如反掌。git
MoyaMapper
是基於Moya和SwiftyJSON封裝的工具,以Moya的plugin的方式來實現間接解析,支持RxSwiftgithubGitHub: MoyaMapperjson
📖 詳細的使用請查看手冊 MoyaMapper.github.ioswift
json
轉 Model
自動映射 與 自定義映射json
中值的類型,Model
中屬性聲明的是什麼類型,它就是什麼類型Data
字典
JSON
json字符串
Model
互轉Moya.Response
,拒絕各類網絡問題致使 Response
爲 nil
,將各式各樣的緣由致使的數據加載失敗進行統一處理,開發者只須要關注 Response
JSON
、 Number
、String
、 Bool
、 Moya.Response
)附:插件 MoyaMapperPlugin 的詳細使用api
一、定義適用於項目接口的 ModelableParameterType
數組
// statusCodeKey、tipStrKey、 modelKey 能夠任意指定級別的路徑,如: "error>used"
struct NetParameter : ModelableParameterType {
var successValue = "000"
var statusCodeKey = "retStatus"
var tipStrKey = "retMsg"
var modelKey = "retBody"
}
複製代碼
二、在 MoyaProvider
中使用 MoyaMapperPlugin
插件,並指定 ModelableParameterType
緩存
let lxfNetTool = MoyaProvider<LXFNetworkTool>(plugins: [MoyaMapperPlugin(NetParameter())])
複製代碼
❗ 使用 MoyaMapperPlugin
插件是整個 MoyaMapper
的核心所在!ruby
Model
需遵照Modelable
協議bash
MoyaMapper
支持模型自動映射 和 自定義映射- 不須要考慮源json數據的真實類型,這裏統一按
Model
中屬性聲明的類型進行轉換
一、通常狀況下以下寫法便可服務器
struct CompanyModel: Modelable {
var name : String = ""
var catchPhrase : String = ""
init() { }
}
複製代碼
二、若是自定義映射,則能夠實現方法 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
}
}
複製代碼
三、支持模型嵌套
struct UserModel: Modelable {
var id : String = ""
var name : String = ""
var company : CompanyModel = CompanyModel()
init() { }
}
複製代碼
一、如下示例皆使用了
MoyaMapperPlugin
,因此不須要指定解析路徑
二、若是沒有使用
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) 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
取值是取不到的。
內部緩存過程:
// 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
MoyaMapper默認只安裝Core下的文件
pod 'MoyaMapper'
複製代碼
pod 'MoyaMapper/Rx'
複製代碼
pod 'MoyaMapper/MMCache'
複製代碼
pod 'MoyaMapper/RxCache'
複製代碼