在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方法。在其中做某些事情就行了。
好了这里差不多包含了主要的使用方式,其他小情况请大伙参考 官方文档 吧!裸
小伙伴们如果感觉文章可以,可以关注博主博客
小伙伴们多多关注博主微博,探索博主内心世界
如要转载请注明出处。