在Swift中我们发送网络请求一般都是使用一个第三方库 Alamofire ,设置好URL和parameter然后发送网络请求,就像下面这样:
let param = ["iid": iid]
let url = baseURL + articlePath
Alamofire.request(url, parameters: param).responseJSON { (response) in
switch response.result {
case .success(let value):
....
case .failure(let error):
}
}
这些代码一般都是写在项目的Service或者ViewModel文件中,随着项目的增大每一个Service文件或者ViewModel文件中就会有很多不同的网络请求,每一次网络请求都不免会写这样的代码,那么项目中的网络请求就会变得很乱。
那么这时候一般我们会在项目中添加一个网络请求层,来管理网络请求,一般会叫 APIManager 或者 NetworkModel ,但是这样子还是会有一点不好:
但是Moya是专业处理这些问题而生滴。Moya有以下优点:
Moya是作用在Alamofire之上,让我们不再直接去使用Alamofire了,Moya也就可以看做我们的网络管理层,只不过他拥有更好更清晰的网络管理。可以看到下图,我们的APP直接操作Moya,让Moya去管理请求,不在跟Alamofire进行接触
我们要怎么使用Moya进行请求呢?很简单。(参考官方Demo)比如我们要查询github上一个人的userProfile和一个人的repository。大伙可以想一下,我们如果直接使用Alamfire怎么去请求的。然而我们看下Moya怎么做:
首先我们需要声明一个enum来对请求进行明确分类。
public enumGitHub{
case userProfile(String) //请求profile
case userRepositories(String) //请求repository
}
然后我们需要让这个enum实现一个协议TargetType,点进去可以看到TargetType定义了我们发送一个网络请求所需要的东西,什么baseURL,parameter,method等一些计算性属性,我们要做的就是去实现这些东西,当然有带默认值的我们可以不去实现。相信下面代码中的东西大家都能看的懂,下面定义了每一个请求所需要的基本数据。
extensionGitHub:TargetType{
public var baseURL: URL { return URL(string: "https://api.github.com")! }
public var path: String {
switch self {
case .userProfile(let name):
return "/users//(name.urlEscaped)"
case .userRepositories(let name):
return "/users//(name.urlEscaped)/repos"
}
}
public var method: Moya.Method {
return .get
}
public var parameters: [String: Any]? {
switch self {
case .userRepositories(_):
return ["sort": "pushed"]
default:
return nil
}
}
public var parameterEncoding: ParameterEncoding {
return URLEncoding.default
}
public var task: Task {
return .request
}
public var validate: Bool {
switch self {
return false
}
}
//这个就是做单元测试模拟的数据,必须要实现,只在单元测试文件中有作用
public var sampleData: Data {
switch self {
case .userProfile(let name):
return "[{/"name/": /"Repo Name/"}]".data(using: String.Encoding.utf8)!
case .userRepositories(_):
return ....
}
}
}
当我们实现了这个这个enum,我们就相当于完成了一个网络请求所需要的一个 endpoint ,endpoint其实就是一个结构,包含了我们一个请求所需要的基本信息,比如url,parameter等。endpoint好了这时候我们就需要一个工具去发送这个请求。于是Moya提供一个发送请求的 Provider
let provider = MoyaProvider<GitHub>()
// 使用我们的provider进行网络请求,请求某个人的仓库
provider.request(.userRepositories("codertian")) { result in
switch result {
case let .success(response):
.......
case let .failure(error):
......
}
}
当然上面的Provider的init方法有很多参数,但是都有默认值。
public init(endpointClosure: @escaping EndpointClosure = MoyaProvider.defaultEndpointMapping,
requestClosure: @escaping RequestClosure = MoyaProvider.defaultRequestMapping,
stubClosure: @escaping StubClosure = MoyaProvider.neverStub,
manager: Manager = MoyaProvider<Target>.defaultAlamofireManager(),
plugins: [PluginType] = [],
trackInflights: Bool = false) {
....
}
你可以点击进去看下每一个默认值都做了些啥,当然我们也可以自己去实现这些参数,然后init的时候把自定义参数传递进去。比如我们自定义一个 endpointClouse ,然后创建Provider的时候传递进去就行了。
let endpointClosure = { (target: GitHub) -> Endpoint<GitHub> in
return Endpoint<GitHub>(url: url(target), sampleResponseClosure:{.networkResponse(200, target.sampleData)}, method: target.method, parameters: target.parameters)
}
let GitHubProvider = MoyaProvider(endpointClosure: endpointClosure)
Moya也有自己的RxSwift的扩展,不懂RxSwift的童鞋可以看下我们博客中的关于RxSwift库介绍的文章。Moya使用RxSwift很简单,如下所示我们只需要对请求结果进行监听就行了
let provider = RxMoyaProvider<NewsService>()//要使用RxMoyaProvider创建provider
provider.request(.zen).subscribe { event in
switch event {
case .next(let response):
// do something with the data
case .error(let error):
// handle the error
}
}
我们还可以对Observable进行扩展,自定义一些自己流水线操作,比如当捕获到错误的时候弹出一个toast,定义如下。
extensionObservable{
funcshowErrorToast() -> Observable<Element> {
return self.doOn { event in
switch event {
case .error(let e):
...
}
}
}
}
那么使用的时候我们就可以使用这种方式调用
provider.request(.resetPassword(email: textField.text!))
.filterSuccessfulStatusCodes()
.showErrorToast() //捕获到错误就会自动弹出
.subscribeNext { response in
.....
}
Moya也为我们提供了很多Observable的扩展,让我们能更轻松的处理MoyaResponse,常用的如下:
具体可以参考 官方文档
设置请求参数的时候有一点需要注意。参数设置要使用以下方式
public var parameters: [String: Any]? {
switch self {
case .users(let limit):
var params: [String: Any] = [:]
params["limit"] = limit
return params
.......
}
不能使用下面这种方式,因为上面的limit有可能为nil,这样子下面这个字典就会报错了。
public var parameters: [String: Any]? {
switch self {
case .users(let limit):
let params: [String: Any] = ["limit": limit]
return params
.......
}
Moya在初始化Provider的时候可以传入一些插件,Moya库中默认有4个插件。
我们也可以自定义自己的插件,比如我们 官方例子 中当请求失败的时候弹出一个alert
final classRequestAlertPlugin:PluginType{
private let viewController: UIViewController
init(viewController: UIViewController) {
self.viewController = viewController
}
funcwillSend(request: RequestType, target: TargetType) {
........//实现发送请求前需要做的事情
}
funcdidReceive(result: Result<Response, MoyaError>, target: TargetType) {
guard case Result.failure(_) = result else { return }//只监听失败
// 弹出Alert
let alertViewController = UIAlertController(title: "Error", message: "Request failed with status code:/(error.response?.statusCode ??0)", preferredStyle: .alert)
alertViewController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
viewController.present(viewControllerToPresent: alertViewController, animated: true)
}
}
实现PluginType协议,然后再实现两个请求监听方法willSend和didReceive方法。在其中做某些事情就行了。
好了这里差不多包含了主要的使用方式,其他小情况请大伙参考 官方文档 吧!裸
小伙伴们如果感觉文章可以,可以关注博主博客
小伙伴们多多关注博主微博,探索博主内心世界
如要转载请注明出处。