YandexMoneyCoreApi1.8.1

YandexMoneyCoreApi1.8.1

tverdokhlebAlexandr ZalutskiyMikhail KaravanovAlexander KurginDmitriy Shakolo 维护。



  • YandexMoney

Yandex Money 核心API库

Build Status Carthage Compatible GitHub tag CocoaPods CocoaPods license

概念

ApiSession

ApiSession - 在 URLSession 之上的抽象层。 ApiSession 可以执行 ApiMethod,可以取消所有正在运行中的请求。

要创建一个新的会话,需要创建 HostProviderHostProvider 是一个接口,它能够根据键返回所需的主机。您可以把它看作一个字典,它能存储和检索主机名和值。

如果您正在测试环境中工作,您可以实现一个单独的 HostProvider,该 HostProvider 将会话的所有方法都导向测试环境,而不是生产环境。根据不同的环境,您只需在 ApiSession 中 внедр不同版本的 HostProvider

ApiMethod

ApiMethod - 您的请求服务器的模型。每个 ApiMethod 都应该有自己的 Response。==> 对于每个方法,可以描述服务器的一个唯一响应。

要实现ApiMethod,您需要定义以下属性

  • hostProviderKey - 该键用于由HostProvider返回用于请求的host。
  • httpMethod - HTTP协议的方法(例如:.post.get)。
  • parametersEncoding - 参数Encoder。在CoreApi中,已有两个现成的EncoderJsonParametersEncoderQueryParametersEncoder

然后是方法

  • urlInfo(from hostProvider: HostProvider) throws -> URLInfo - 该方法使用HostProvider返回请求的URL。

建议在此方法中使用hostProviderKey属性,除非url是从外部传入的。

可选

  • headers - 存储请求头的结构。

ApiMethod需要实现Encodable协议。在encode(to:)方法中编码的内容将被发送到parametersEncoding,并嵌入到请求的相应部分。

例如,如果您使用QueryParametersEncoder,则数据将出现在查询参数字符串中,而使用JsonParametersEncoder时,它们将作为json格式嵌入到请求体中。

ApiResponse

ApiResponse - 服务器响应模型。为了方便,建议继承此接口并创建自己的。

以下是一个示例。您可能有几个不同的api,每个api都返回特定的错误或按照其自己的格式返回错误。最终,对于每个Api,您都可以创建一个继承自ApiResponse的协议,并在其中定义自己的processmakeResponse方法。这样,您就不需要在每个模型中实现相同错误处理代码了。下面是使用ApiResponse实现自己协议的一个示例。

public protocol WalletApiResponse: ApiResponse {}
extension WalletApiResponse {
    public static func process(response: HTTPURLResponse?, data: Data?, error: Error?) -> Result<Self> {
        var result: Result<Self>
        if let response = response,
           let data = data,
           let error = WalletAuthApiError.makeResponse(response: response, data: data) {
            result = .left(error)
        } else if let response = response,
                  let data = data,
                  let error = self.makeSpecificError(response: response, data: data) {
            result = .left(error)
        } else if let response = response,
                  let data = data,
                  let serializedData = self.makeResponse(response: response, data: data) {
            result = .right(serializedData)
        } else if let error = error {
            result = .left(error)
        } else {
            result = .left(WalletApiError.mappingError)
        }
        return result
    }
}

在这个例子中,我们首先

  1. 尝试解析通用的api错误。
  2. 尝试解析特定于api方法的错误。
  3. 尝试解析服务器的响应。
  4. 如果发生ApiSession错误,则返回该错误。
  5. 如果无法解析任何内容,并且没有发生ApiSession层的错误,则返回映射错误。

为了不在每个错误和每个响应中实现makeResponse(response:data:)方法,有几种辅助协议可供使用

  • JsonApiResponse - 尝试使用Decodable协议从data中获取模型。
  • TextApiResponse - 将data转换为utf8字符串并调用初始化器init?(text:)。如果response包含textEncodingName,则使用服务器的响应编码而不是utf8。

您也可以创建自己的协议并实现其makeResponse(response:data:)方法,如果标准协议不适合您。例如,如果您需要解析XML。

示例

/// Модель ответа от сервера.
public struct PaymentMethod {
    public let type: PaymentMethodType
    public let id: String
    public init(type: PaymentMethodType,
                id: String) {
        self.type = type
        self.id = id
    }
    /// Модель запроса на сервер.
    public struct Method {
        public let oauthToken: String
        public init(oauthToken: String) {
            self.oauthToken = oauthToken
        }
    }
}
// Парсим ответ от сервера.
extension PaymentMethod: Decodable {
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try container.decode(PaymentMethodType.self, forKey: .type)
        let id = try container.decode(String.self, forKey: .id)
        self.init(
            type: type,
            id: id
        )
    }
    private enum CodingKeys: String, CodingKey {
        case type
        case id
    }
}
// Реализовываем ApiResponse для модели ответа.
extension PaymentMethod: PaymentsApiResponse, JsonApiResponse {}
// Реализовываем ApiMethod для модели запроса.
extension PaymentMethod.Method: ApiMethod {
    public typealias Response = PaymentMethod
    public var hostProviderKey: String {
        return Constants.paymentsApiMethodsKey
    }
    public var httpMethod: HTTPMethod {
        return .get
    }
    public var parametersEncoding: ParametersEncoding {
        return QueryParametersEncoder()
    }
    public var headers: Headers {
        let headers = Headers([
            AuthorizationConstants.authorization: AuthorizationConstants.basicAuthorizationPrefix + oauthToken,
        ])
        return headers
    }
    public func urlInfo(from hostProvider: HostProvider) throws -> URLInfo {
        return .components(host: try hostProvider.host(for: hostProviderKey),
                           path: "/payment_method")
    }
}
// Перегружаем стандартные `encode(to:)` и `init(from:)` чтобы поле `oauthToken` не попало в 
// query параметры.
extension PaymentMethod.Method: Encodable, Decodable {
    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
    }
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.init(oauthToken: "")
    }
    private enum CodingKeys: String, CodingKey {}
}