Moya是一个高度抽象的网络库,他的理念是让你不用关心网络请求的底层的实现细节,只用定义你关心的业务。且Moya采用桥接和组合来进行封装(默认桥接了Alamofire),使得Moya非常好扩展,让你不用修改Moya源码就可以轻易定制。官方给出几个Moya主要优点:
- 编译时检查API endpoint权限
- 让你使用枚举定义各种不同Target, endpoints
- 把stubs当做一等公民对待,因此测试超级简单。
Target
开始Moya之旅的第一步便是,建立一个Enum的Target,这个Target便是你网络请求相关行为的定义。Target必须实现TargetType协议。
public protocol TargetType {
var baseURL: NSURL { get }
var path: String { get }
var method: Moya.Method { get }
var parameters: [String: AnyObject]? { get }
var sampleData: NSData { get }
}
例如有一个AccountAPI模块,模块实现注册登录的功能。所以第一件事情,我们需要定义一个Target
enum AccountAPI {
case Login(userName: String, passwd: String)
case Register(userName: String, passwd: String)
}
extension AccountAPI: TargetType {
var baseURL: NSURL {
return NSURL(string: "https://www.myapp.com")!
}
var path: String {
switch self {
case .Login:
return "/login"
case .Register:
return "/register"
}
}
var method: Moya.Method {
return .GET
}
var parameters: [String: AnyObject]? {
switch self {
case .Login:
return nil
case .Register(let userName, let passwd):
return ["username": userName, "password": passwd]
}
}
var sampleData: NSData {
switch self {
case .Login:
return "{'code': 1,6'Token':'123455'}".dataUsingEncoding(NSUTF8StringEncoding)!
case .Register(let userName, let passwd):
return "找不到数据"
}
}
}
主要是实现了TargetType协议,里面的网址和内容,是随便写的,可能不make sence(不合理), 但 仅仅是做一个例子而已。
Providers
Providers是Moya中的核心,Moya中所有的API请求都是通过Provider来发起的。因此大多数时候,你的代码请求像这样:
let provider = MoyaProvider<AccountAPI>()
provider.request(.Login) { result in
// `result` is either .Success(response) or .Failure(error)
}
我们初始化了一个AccountAPI的Provider,并且调用了Login请求。怎么样?干净简单吧!
从Provider的构造函数说起
Provider真正做的事情可以用一个流来表示:Target -> Endpoint -> Request 。在这个例子中,它将AccountAPI转换成Endpoint, 再将其转换成为NSRURLRequest。最后将这个NSRURLRequest交给Alamofire去进行网络请求。
我们从Provider的构造函数开始切入,一步一步地扒开它。
//Moya.swift
public init(endpointClosure: EndpointClosure = MoyaProvider.DefaultEndpointMapping,
requestClosure: RequestClosure = MoyaProvider.DefaultRequestMapping,
stubClosure: StubClosure = MoyaProvider.NeverStub,
manager: Manager = MoyaProvider<Target>.DefaultAlamofireManager(),
plugins: [PluginType] = [])
首先我们发现的是3个Closure:endpointClosure、requestClosure、stubClosure。这3个Closure是让我们定制请求和进行测试时用的。非常有用,后面细说。
然后是一个Manager,Manager是真正用来网络请求的类,Moya自己并不提供Manager类,Moya只是对其他网络请求类进行了简单的桥接。这么做是为了让调用方可以轻易地定制、更换网络请求的库。比如你不想用Alamofire,可以十分简单的换成其他库
最后是一个类型为PluginType的数组。Moya提供了一个插件机制,使我们可以建立自己的插件类来做一些额外的事情。比如写Log,显示“菊花”等。抽离出Plugin层的目的,就是让Provider职责单一,满足开闭原则。把和自己网络无关的行为抽离。避免各种业务揉在一起不利于扩展。
先来看看第一个EndpointClosure
EndpointClosure
//Moya.swift
public typealias EndpointClosure = Target -> Endpoint<Target>
EndpointClosure这个闭包,输入是一个Target,返回Endpoint。这就是我们前面说的Target -> Endpoint的转换,那么Endpoint是个什么鬼?
Endpoint 是Moya最终进行网络请求前的一种数据结构,它保存了这些数据:
- URL
- HTTP请求方式 (GET, POST, etc).
- 本次请求的参数
- 参数的编码方式 (URL, JSON, custom, etc).
- stub数据的 response(测试用的)