RxSwift学习之旅 - Moya + ObjectMapper

什么是Moya?

Moya是一个基于Alamofire封装的一个抽象层,让我们更加关注自己的业务处理。

image

同时还可以通过中间件的方式拦截和修改请求,mock数据等等。

image

使用Moya

来看一个简单的例子吧,在Moya中要发送一个网络请求,需要定义个枚举并实现TargetType协议中的方法。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//根据不同的枚举类型来配置不同的参数
enum User{
case list(Int,Int)
}

extension User : TargetType{
var baseURL : URL{
return URL(string: "http://www.alonemonkey.com")!
}

var path: String{
switch self {
case .list:
return "userlist"
}
}

var method: Moya.Method{
switch self {
case .list:
return .get
}
}

var parameters: [String: Any]?{
switch self{
case .list(let start, let size):
return ["start": start, "size": size]
}
}

var parameterEncoding: ParameterEncoding{
return URLEncoding.default
}

var task: Task{
return .request
}

var sampleData: Data{
switch self {
case .list(_, _):
if let path = Bundle.main.path(forResource: "UserList", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
return data
} catch let error {
print(error.localizedDescription)
}
}
return Data()
}
}
}

上面参数的函数大家看名字也就知道了。

然后需要一个MoyaProvider对象,这里使用RxMoyaProvider来进行响应式编程。

1
let UserProvider = RxMoyaProvider<User>(stubClosure: MoyaProvider.immediatelyStub)

stubClosure: MoyaProvider.immediatelyStub表示使用本地mock数据。

然后使用生成的RxMoyaProvider对象发起请求。

1
2
3
4
5
6
7
8
let disposeBag = DisposeBag()
UserProvider
.request(.list(0, 10))
.mapJSON()
.subscribe{
print($0)
}
.disposed(by: disposeBag)

运行就可以在控制台打印出本地mockjson数据了。

ObjectMapper

获取了json数据后,需要把数据解析成对应的对象,这里我们使用ObjectMapper。已有Moya-ObjectMapper, 可以满足我们的需求。

安装

1
pod 'Moya-ObjectMapper/RxSwift'

首先定义Model:

1
2
3
4
5
6
7
8
9
10
11
struct User : Mappable{
var name: String!
var age: Int!

init?(map: Map) {}

mutating func mapping(map: Map){
name <- map["name"]
age <- map["age"]
}
}

然后在请求的时候可以指定Model去解析数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
UserProvider
.request(.list(0, 10))
.mapArray(User.self)
.subscribe{
event in
switch event{
case .next(let users):
for user in users{
print("\(user.name) \(user.age)")
}
default:
break
}
}
.disposed(by: disposeBag)

固定格式

上面都是直接就返回了一个User的数组,但是实际开发中,都会返回一个固定的格式,表示状态、消息、结果,像下面这种:

1
2
3
4
5
{
code: 300,
message: "xxxxxx",
result: xxxx //这里才是我们需要的东西
}

对于这情况怎么处理呢?

首先看一下mapArray是怎么处理的把:

1
2
3
4
5
6
public func mapArray<T: BaseMappable>(_ type: T.Type, context: MapContext? = nil) throws -> [T] {
guard let array = try mapJSON() as? [[String : Any]], let objects = Mapper<T>(context: context).mapArray(JSONArray: array) else {
throw MoyaError.jsonMapping(self)
}
return objects
}

首先调用了mapJSON转成json,然后调用Mapper<T>(context: context).mapArray(JSONArray: array)转成对象数组。

我们也可以写一个类似的方法,首先定义需要解析的公共部分结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Status : Mappable{
var code : Int!
var message : String?
var result : Any?

init?(map: Map) {}

mutating func mapping(map: Map) {
code <- map["code"]
message <- map["message"]
result <- map["result"]
}
}

然后自己写一个mapResult:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public extension Response {
public func mapResult<T: BaseMappable>(_ type: T.Type, context: MapContext? = nil) throws -> [T] {

let status = try mapObject(Status.self)

guard let array = status.result as? [[String : Any]], let objects = Mapper<T>(context: context).mapArray(JSONArray: array) else {
throw AMError.ParseResultError(status)
}

return objects
}
}

public extension ObservableType where E == Response {
public func mapResult<T: BaseMappable>(_ type: T.Type, context: MapContext? = nil) -> Observable<[T]> {
return flatMap { response -> Observable<[T]> in
return Observable.just(try response.mapResult(T.self, context: context))
}
}
}

然后就能获取到对应的结果了,如果出错了的话,也可以获取到错误信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.subscribe{
event in
switch event{
case .next(let users):
for user in users{
print("\(user.name) \(user.age)")
}
case .error(let error):
var message = "出错了!"
if let amerror = error as? AMError, let msg = amerror.message{
message = msg
}
print(message)
default:
break
}
}

大家想一想如果result返回的是单个对象的话,应该怎么写?

总结

Moya本身提供了强大的扩展能力,可以对其进行扩展,再加上RxObjectMapper,我们可以对网络请求的发送、处理以及解析通过链式调用的方法来处理,写出优雅的代码。

代码见github:

RxSwiftMoya

AloneMonkey wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!