转载

【iOS开发】Moya入坑记-用法解读篇

在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进行接触

【iOS开发】Moya入坑记-用法解读篇

网络请求

我们要怎么使用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)

RxSwift

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,常用的如下:

  • filter(statusCodes:) 过滤response状态码
  • filterSuccessfulStatusCodes() 过滤状态码为请求成功的
  • mapJSON() 将请求response转化为JSON格式
  • mapString() 将请求response转化为String格式

具体可以参考 官方文档

paramter

设置请求参数的时候有一点需要注意。参数设置要使用以下方式

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个插件。

  • AccessTokenPlugin 管理AccessToken的插件
  • CredentialsPlugin 管理认证的插件
  • NetworkActivityPlugin 管理网络状态的插件
  • NetworkLoggerPlugin 管理网络log的插件

我们也可以自定义自己的插件,比如我们 官方例子 中当请求失败的时候弹出一个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方法。在其中做某些事情就行了。

好了这里差不多包含了主要的使用方式,其他小情况请大伙参考 官方文档 吧!裸

小伙伴们如果感觉文章可以,可以关注博主博客

小伙伴们多多关注博主微博,探索博主内心世界

如要转载请注明出处。

原文  http://www.codertian.com/2017/01/21/iOS-Moya-ru-keng-usage/
正文到此结束
Loading...