Back to Moya

高级用法 - 为多个target在同一个`Provider`中使用而采用 `MultiTarget` .

docs_CN/Examples/MultiTarget.md

15.0.34.1 KB
Original Source

高级用法 - 为多个target在同一个Provider中使用而采用 MultiTarget .

当你有很多endpoints你可能以一个非常长的provider和在上百个case里多次切换而告终。你当然可以把这些逻辑分割到多个target中,但是你同时不得不使用多个provider。这会让你的app逻辑变复杂,并且如果你希望为它们配置相同的插件/闭包,你需要花费额外的功夫来管理它们。为了解决这个麻烦,我们可以使用内置的MultiTarget枚举类型,它是相当的易用。

首先, 我们需要定义一个使 multiple target的provider:

swift
let provider = MoyaProvider<MultiTarget>()

然后, 当你想启动请求的时候, 你需要替换

swift
provider.request(.zen) { result in
    // do something with `result`
}

swift
provider.request(MultiTarget(GitHub.zen)) { result in
    // do something with `result`
}

到此配置结束! 在您的app中采用它是相当的简单 而且如果您有很多想分割的 endpoints - 这是一个相当完美的解决方法. 如果你想看到这个API的作用, 查看我们的 Demo 项目, 它里面有两个 target: 一个是 Demo, 它使用的Moya的基础版; 另一个是DemoMultiTarget,它使用MultiTarget的修改版本 。

在多目标中使用 关联类型

使用Moya能让您在调用网络请求时静态地验证参数。您可能想扩展Moya的 TargetType 并验证您的自定义类型。一种用例就是让请求方法返回基于请求的不同来序列化模型,而不是使用默认的MoyaResponse来返回。这可以通过在TargetType中添加 associatedtype来解决

swift
protocol DecodableTargetType: Moya.TargetType {
    associatedType ResultType: SomeJSONDecodableProtocolConformance
}

enum UserApi: DecodableTargetType {
    case get(id: Int)
    case update(id: Int, name: String)
    ...

    var baseURL: URL { ... }
    var path: String { switch self ... }
    var method: Moya.Method { ... }

    typealias ResultType = UserModel
}

由于 associatedtype, MultiTarget 不能被当做 DecodableTargetType. 但是, 我们可以使用 MultiMoyaProvider 变体. 它不需要泛型参数. 所以, 任何遵循TargetType的实例对象都可以调用这个请求. 使用 MultiMoyaProvider 允许你编写可以使用associatedtype的请求包装器.

比如, 我们可以创建一个返回ResultTyperequestDecoded方法 来替换 MoyaResponse

swift
extension MultiMoyaProvider {
    func requestDecoded<T: DecodableTargetType>(_ target: T, completion: @escaping (_ result: Result<[T.ResultType], Moya.Error>) -> ()) -> Cancellable {
        request(target) { result in
            switch result {
            case .success(let response):
                if let parsed = T.ResultType.parse(try! response.mapJSON()) {
                    completion(.success(parsed))
                } else {
                    completion(.failure(.jsonMapping(response)))
                }
            case .failure(let error):
                completion(.failure(error))
            }
        }
    }
}

其美妙之处在于,回调中的输入类型是由已传入的目标隐式地确定的.

您可以传入任何 DecodableTargetType 类型来启动一个请求

swift
let provider = MultiMoyaProvider()
provider.requestDecoded(UserApi.get(id: 1)) { result in
    switch result {
    case .success(let user):
      // type of `user` is implicitly `UserModel`. Using any other type results
      // in compile error
      print(user.name)
    }
}

当使用 associatedtype, 您将需要为不同的类型定义不同的target 。 比如, 假设我们另外一个target叫 SessionApi

swift
struct SessionApi: DecodableTargetType {
    typealias ResultType = SessionModel
}

它定义了一个不同的 ResultType. 我们可以使用相同的 MultiMoyaProvider 实例对象

swift
provider.requestDecoded(SessionApi.get) { result in
    switch result {
    case .success(let session):
        // type of `user` is implicitly `SessionModel` here
    }
}