Moya
Plays an important role in network interaction in Swift development, but there are disadvantages, such as the return when the network is unavailableResponse
为nil
And then you have to parse the correspondingError
Codable
It can help us parse data quickly, but if the declared property type is different from that in JSON, it will not parse properly. Moreover, the processing of custom attribute names in the model is also very tedious
There are many solutions, but I am used to using MoyaMapper, which can not only solve the above problems, but also provide a variety of convenient methods for model transformation, data exchange, and arbitrary storage of various data types. Controlling Moya’s web requests, data parsing, and caching is a breeze.
MoyaMapper is based on Moya and SwiftyJSON package tool, Moya plugin to achieve indirect parsing, support RxSwift
GitHub: MoyaMapper
📖 please refer to the manual moyamapper.github. IO for details
The characteristics of
- support
json
转Model
Automatic mapping and custom mapping - ignore
json
The type of the median,Model
What type is declared by an attribute in - support
Data
The dictionary
JSON
Json string
Model
Transfers between - Plug-in mode, all-round protection
Moya.Response
Reject all kinds of network problems caused byResponse
为nil
, the data loading failure caused by various reasons will be uniformly handled, developers only need to pay attention toResponse
- Optional – Support arbitrary data caching (
JSON
、Number
、String
,Bool
,Moya.Response
) - Optional – Network request caching is supported
Data parsing
First, plug-in injection
Attached: plugin MoyaMapperPlugin detailed use
Define ModelableParameterType for the project interface
// statusCodeKey, tipStrKey, modelKey can specify any level of path, such as "error>used"
struct NetParameter : ModelableParameterType {
var successValue = "000"
var statusCodeKey = "retStatus"
var tipStrKey = "retMsg"
var modelKey = "retBody"
}
Copy the code
Use MoyaMapperPlugin in MoyaProvider and specify ModelableParameterType
let lxfNetTool = MoyaProvider<LXFNetworkTool>(plugins: [MoyaMapperPlugin(NetParameter()))Copy the code
❗ Using MoyaMapperPlugin is the core of the whole MoyaMapper!
Ii. Model Statement
The Model complies with the Modelable protocol
MoyaMapper
Support for model automatic mapping and custom mapping- Regardless of the actual type of the source JSON data, click
Model
To the type declared by the property in
1. In general, it can be written as follows
struct CompanyModel: Modelable {
var name : String = ""
var catchPhrase : String = ""
init() {}}Copy the code
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
}
}
Copy the code
3. Support model nesting
struct UserModel: Modelable {
var id : String = ""
var name : String = ""
var company : CompanyModel = CompanyModel(a)init() {}}Copy the code
3
1. The following examples all use MoyaMapperPlugin, so there is no need to specify a resolution path
2. If MoyaMapperPlugin is not used, specify the parsing path; otherwise, the parsing fails
Ps: Path resolution can use the form A > B to solve the multilevel path problem
The following list shows the resolution methods
methods | Description (RxSwift support) |
---|---|
toJSON | The Response to JSON (toJSON | rx.toJSON) |
fetchString | Gets the string for the specified path (fetchString | rx.fetchString) |
fetchJSONString | Gets the raw JSON string for the specified path (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) |
❗ Except for fetchJSONString, where the default resolution path is the root path, the default resolution path for all methods is modelKey in the plug-in object
If the json data structure after the interface request is similar to the following figure, MoyaMapper is most appropriate
// Normal
let model = response.mapObject(MMModel.self)
print("name -- \(model.name)")
print("github -- \(model.github)")
/ / print json
print(response.fetchJSONString())
// Rx
rxRequest.mapObject(MMModel.self)
.subscribe(onSuccess: { (model) in
print("name -- \(model.name)")
print("github -- \(model.github)")
}).disposed(by: disposeBag)
Copy the code
Attached: fetchJSONString detailed use
// Normal
let models = response.mapArray(MMModel.self)
let name = models[0].name
print("count -- \(models.count)")
print("name -- \(name)")
// Prints the name of the first in the JSON model array
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)
Copy the code
Attached: Detailed instructions for mapArray
// Normal
let (isSuccess, tipStr) = response.mapResult()
print("isSuccess -- \(isSuccess)")
print("tipStr -- \(tipStr)")
// Rx
rxRequest.mapResult()
.subscribe(onSuccess: { (isSuccess, tipStr) in
print("isSuccess -- \(isSuccess)") // Whether it is "000"
print("retMsg -- \(retMsg)") // "Necessary parameters are missing"
}).disposed(by: disposeBag)
Copy the code
Attached: detailed instructions for using mapResult
Unified processing of network request results
In the actual use of APP, we will encounter a variety of network request results, such as: the server is down, the mobile phone has no network, and the Response returned by Moya is nil, so we have to judge Error. But using the MoyaMapperPlugin lets us focus only on Response
// The MoyaMapperPlugin initialization method
public init<T: ModelableParameterType> (_ type: T,
transformError: Bool = true
)
type : ModelableParameterTypeDefines field paths that are used to parse data globally. TransformError:BoolWhether to automatically convert the request result when a network request fails. The default value istrue
Copy the code
- When the request fails
result.response
为nil
, according to thetransformError
Whether it istrue
Determines whether to create a customresponse
And back out.
Data content that can be requested
Offline now, request data again
-
Normally, when you’re not doing anything and you’re not doing anything, Response is nil
-
After processing by MoyaMapperPlugin, the transformed Response can be obtained, as shown in the figure
RetStatus is mmStatuscode. loadFail regardless of whether the server or its own network is faulty, but errorDescription remains the same and is assigned to retMsg.
retStatus
The value will be from the enumerationMMStatusCode
To takeloadFail.rawValue
, i.e.,700
- Take type for
ModelableParameterType
的type
中statusCodeKey
The value specified is the key name,retMsg
Also in the same way
Ps: this time can pass judgment retStatus or response. The statusCode whether with MMStatusCode loadFail. RawValue same to judge whether the failure of blank page display placeholder figure
enum MMStatusCode: Int {
case cache = 230
case loadFail = 700
}
Copy the code
LoadFail enumeration MMStatusCode, in addition to, and the cache, we already know loadFail data loading in failure occur, when the cache is out? Don’t worry. You’ll find out in the next section.
Data cache
First, basic use
// cache @discardableresult mmcache.shared. cache 'XXX' (value: XXX, key: String, cacheContainer: MMCache.CacheContainer =.ram) -> Bool MMCache. Shared. fetch 'XXX' Cache(key: String, CacheContainer: String) MMCache.CacheContainer = .RAM)Copy the code
A successful cache returns a Bool, which is not accepted here
Types supported by XXX | |
---|---|
Bool | – |
Float | – |
Double | – |
String | – |
JSON | – |
Modelable | [Modelable] |
Moya.Response | – |
Int | UInt |
Int8 | UInt8 |
Int16 | UInt16 |
Int32 | UInt32 |
Int64 | UInt64 |
Except for moya. Response, all other types are cached through JSON
So, if you want to clear these types of caches, just call the following method
@discardableResult
func removeJSONCache(_ key: String, cacheContainer: MMCache.CacheContainer = .RAM) -> Bool
@discardableResult
func removeAllJSONCache(cacheContainer: MMCache.CacheContainer = .RAM) -> Bool
Copy the code
To clear moya. Response, use the following two methods
@discardableResult
func removeResponseCache(_ key: String) -> Bool
@discardableResult
func removeAllResponseCache(a) -> Bool
Copy the code
Look again at MMCache.CacheContainer
enum CacheContainer {
case RAM // The container is only cached in memory
case hybrid // A container cached in memory and disk
}
Copy the code
The two containers are not connected, that is, even if the key is the same, the value cannot be obtained through RAM after using hybrid cache.
- RAM: It is only cached in memory, and the cached data will last for the duration of APP use
- Hybrid: Cached in memory and disk. Data can be obtained even after the APP restarts
Second, cache network requests
Internal cache process:
- When the APP starts for the first time and makes a network request, network data will be cached
- When the APP starts again and makes a network request, the cached data will be returned first, and the network data will be returned after the request is successful
- In other cases only network data is loaded
- The cached data is updated with each successful request
// 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>
Copy the code
The Response to the Moya request is actually cached. CacheType: MMCache.CacheKeyType: MMCache.CacheKeyType: MMCache
Here is the definition of 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)
}
Copy the code
If you want to cache the latest page of multi-page list data, default is not appropriate, because the key used by default contains pageIndex, which would not only cache the latest page of data. So you should use base or custom(String)
We can try caching requests
/* * The first time the APP starts and makes a network request, the network data will be cached * * The next time the APP starts and makes a network request, the cache will be loaded first, then the network data will be loaded * in other cases, only the network data will be loaded */ Each time the data is successfully requested, the data will be updated */
lxfNetTool.rx.cacheRequest(.data(type: .all, size: 10, index: 1))
.subscribe(onNext: { response in
log.debug("statusCode -- \(response.statusCode)")
}).disposed(by: disposeBag)
// The traditional way
/* let _ = lxfNetTool.cacheRequest(.data(type: .all, size: 10, index: 1)) { result in guard let resp = result.value else { return } log.debug("statusCode -- \(resp.statusCode)") } */
Copy the code
Print the result
// Use APP statusCode -- 200 for the first time // close and reopen APP, request statusCode -- 230 statusCode -- 200 // Request statusCode -- 200 againCopy the code
Here is 230 MMStatusCode. Cache. RawValue
CocoaPods
- The default installation
MoyaMapper only installs files under Core by default
pod 'MoyaMapper'
Copy the code
- RxSwift expand
pod 'MoyaMapper/Rx'
Copy the code
- Cache expand
pod 'MoyaMapper/MMCache'
Copy the code
- The Rx cache
pod 'MoyaMapper/RxCache'
Copy the code