Fetcher 1.0.0-alpha.1

Fetcher 1.0.0-alpha.1

Tadeas Kriz维护。



Fetcher 1.0.0-alpha.1

Fetcher

CI Status Version License Platform Slack Status

介绍

Fetcher 是一个用于 Swift 的小型 HTTP 网络库。其主要目标是简化像发送 REST 请求这样的常见任务。网络是一个非常复杂的话题,我们的目标不是涵盖所有可以做的事情。但我们提供一组 API,允许您实现所需的内容或自定义 Fetcher 的行为(这很有用,如果您的服务器由于某种原因不遵守任何标准)。

主要特性

  • 易于使用的 API
  • 编译时安全性(尽可能多)
  • 使用 DataMapper 抽象数据编码
  • 几乎一切都可以自定义
  • 后台线程上的数据映射
  • 按照 DRY 原则设计
  • 支持 RxSwift

变更日志

所有变更和新功能的列表可以在此处找到。

需求

  • Swift 3
  • iOS 8+

安装

CocoaPods

Fetcher 可以通过 CocoaPods 获取。要安装它,只需将以下行添加到您的 Podfile 中的测试目标

pod "Fetcher"

这会自动包含 "Core" 和 "AlamofireRequestPerformer" 子组件。

如果您只想使用 Fetcher 而不带 RequestPerformers(您有自己的实现),则使用

pod "Fetcher/Core"

Fetcher 支持 RxSwift

pod "Fetcher/RxFetcher"

注意:"RxFetcher" 子组件不依赖于 "AlamofireRequestPerformer"。因此,如果您想使用它,需要额外添加

pod "Fetcher"

pod "Fetcher/AlamofireRequestPerformer"

用法

以下是该库提供的所有功能和它们的使用方法。一些用法示例可以在 测试 中找到。本文档假设您已经熟悉 DataMapper。您还应了解一些 HTTP 的基础知识。

快速概览

以下是一个简单的用例示例。它不会解释所有使用的概念。请参阅相应章节以获取解释。

假设我们想要发送 GET 请求,该请求会从我们的服务器检索一些数据。我们的数据如下所示

struct ExampleObject {

    let id: String?
    let text: String?
}

为了模拟我们的服务器,我们将使用 httpbin.org/get。它返回您发送的所有内容。例如,查看 httpbin.org/get?id=1&text=a 的响应。

let fetcher = Fetcher(requestPerformer: AlamofireRequestPerformer())

    fetcher.request(GET<Void, SupportedType>("https://httpbin.org/get", modifiers: URLQueryItem(name: "id", value: "1"), URLQueryItem(name: "text", value: "a")), callback: { response in
    switch response.result {
    case .success(let value):
        let object = ExampleObject(id: value.dictionary?["args"]?.dictionary?["id"]?.string,
                                   text: value.dictionary?["args"]?.dictionary?["text"]?.string)

        print(object) // ExampleObject(id: Optional("1"), text: Optional("a"))
    case .failure(let error):
        // Handle error
        break
    }
})

这可能是您可以编写的最简单的代码(同时也是最丑陋的)。同时,它与 Alamofire 代码几乎没有区别。然而,我们可以稍作改进

let fetcher = Fetcher(requestPerformer: AlamofireRequestPerformer())

fetcher.request(Endpoints.get(id: "1", text: "a"), callback: { response in
    switch response.result {
    case .success(let value):
        // value was already transformed by DataMapper.
        print(value)
    case .failure(let error):
        // Handle error
        break
    }
})

struct Endpoints: EndpointProvider {

    static func get(id: String, text: String) -> GET<Void, ExampleObject> {
        return create("https://httpbin.org/get", modifiers: URLQueryItem(name: "id", value: "\(id)"), URLQueryItem(name: "text", value: "\(text)"))
    }
}

struct ExampleObject: Deserializable {

    let id: String?
    let text: String?

    init(_ data: DeserializableData) throws {
        id = data["args"]["id"].get()
        text = data["args"]["text"].get()
    }
}

在这里使用了DataMapper来去除请求映射,并将端点移动到额外的结构体中,以便于重用。然而,当我们的代码进一步扩展时,仍存在一些问题,这些问题可能会变得更糟。例如,如果我们想添加新的数据对象,那么就必须在每一个地方都写入data["args"],因为服务端API的要求。此外,如果我们添加新的端点,那么基本URL将会出现在多个地方。

let fetcher = Fetcher(requestPerformer: AlamofireRequestPerformer())
fetcher.register(requestEnhancers: HttpBinResponseTranslation())
fetcher.register(requestModifiers: BaseUrl(baseUrl: "https://httpbin.org"))

fetcher.request(Endpoints.post(), input: ExampleObject(id: "1", text: "a")) {
    if let error = $0.result.error {
        // Handle error
    }
}

struct Endpoints: EndpointProvider {

    static func get(id: String, text: String) -> GET<Void, ExampleObject> {
        return create("get", modifiers: URLQueryItem(name: "id", value: "\(id)"), URLQueryItem(name: "text", value: "\(text)"))
    }

    static func post() -> POST<ExampleObject, Void> {
        return create("post")
    }

    static func put() -> PUT<ExampleObject, Void> {
        return create("put")
    }

    static func delete(id: String) -> DELETE<Void, Void> {
        return create("delete", modifiers: URLQueryItem(name: "id", value: "\(id)"))
    }
}

struct HttpBinResponseTranslation: RequestEnhancer {

    func deenhance(response: inout Response<SupportedType>) {
        response = response.map { $0.dictionary?["args"] ?? .null }
    }
}

struct ExampleObject: Mappable {

    var id: String?
    var text: String?

    init(id: String?, text: String?) {
        self.id = id
        self.text = text
    }

    init(_ data: DeserializableData) throws {
        try mapping(data)
    }

    mutating func mapping(_ data: inout MappableData) throws {
        data["id"].map(&id)
        data["text"].map(&text)
    }
}

使用HttpBinResponseTranslation解决了data["args"]的问题,多亏了BaseUrl,我们不再需要在每个地方都编写基本URL(若想了解这一切是如何工作的,请参阅请求增强器)。此例子的另一个重要功能是fetcher.request(Endpoints.post(), input: ExampleObject(id: "1", text: "a")),它展示了如何将一些数据发送到服务器。

获取器

Fetcher负责处理请求和响应。它使用RequestPerformer(见请求执行者)实际完成数据传输。

init中可以传递更多参数

  • objectMapperPolymorph: Polymorph - 用于映射传递给Fetcher的对象的Polymorph(见DataMapper
  • errorHandler: ErrorHandler - 见错误处理器
  • callQueue: DispatchQueue - 几乎所有Fetcher逻辑都完成的队列(包括对象映射)。默认是后台队列。
  • callbackQueue: DispatchQueue - 调用请求中传入的回调的队列。默认是主队列。

Fetcher可以通过register方法进行额外的配置

func register(requestEnhancers: [RequestEnhancer])

func register(requestEnhancers: RequestEnhancer...)

func register(requestModifiers: [RequestModifier])

func register(requestModifiers: RequestModifier...)

这用于添加在每个请求中使用的RequestEnhacerRequestModifier(见请求增强器)。

根据其本质,Fetcher通常只会在代码中的一个地方被创建。但有时你可能想要更多(例如,如果你想要两个具有不同基本URL的服务器)。在这种情况下,将一个Fetcher实例的设置复制到另一个实例可能很有用。这可以通过以下方式实现

init(copy fetcher: Fetcher)

请求

request支持所有类型的数据。它们之间主要的区别在于是否存在输入数据以及是否使用DataMapper。以下是request重载的示例

func request<IN: Serializable, OUT: Deserializable>(_ endpoint: Endpoint<IN, OUT>, input: IN, callback: @escaping (Response<OUT>) -> Void) -> Cancellable

《Endpoint (参见Endpoint) 的第一种泛型类型表示要发送到服务器的数据类型,第二种类型表示服务器将发送回什么数据。 Void 表示没有数据, NSData 表示不使用DataMapperSupportedType 允许您手动执行转换。如果 Endpoint 的输入类型为 Void,则请求没有 input 参数。

request 支持了DataMapper 所有的功能(包括上述提到的异常)。尽管可以创建任意输入和输出类型的 Endpoint,但不能在 request 中使用它们。

request 返回的 Cancellable 可以用来取消请求,如果请求不再必要时。

let cancellable = fetcher.request(Endpoints.endpoint()) { response in
    // Do some stuff
}

cancellable.cancel()

重试

extension Request {

    func retry(max: Int = Int.max, delay: DispatchTimeInterval = .seconds(0), failCallback: () -> Void = {})
}

有时重复执行请求可能会导致完全不同的行为。例如,请求失败因为没有网络连接。在这种情况下,您可能稍后再尝试请求。正是为了这种情况, Request (见 Request) 提供了 retry 方法。

与手动调用请求相同。 max 表示请求将重试多少次。如果达到这个计数,则调用 failCallback 而不是重复请求。每次调用 retry 只重试请求一次。但由于这通常是在回调中完成的(每次尝试都会调用),所以 retry 会循环调用(这里就是 max 作用的场景)。 delay 修改了 Fetcher 在尝试之前应该等待多长时间。从原始请求获得的 Cancallable 对重试请求也有效。示例

fetcher.request(...) { response in
    guard let value = response.result.value else {
        return response.request.retry(max: 3) {
            print("error")
        }
    }

    print(value)
})

在这里,只有在 result 是失败的情况下才会调用 retry。调用 retry 后,此回调结束。下一次调用时会使用不同的 Response,这个过程会重复(在这种情况下最多三次)。如果在所有这些重试之后,result 仍然是失败,则调用 print("error"),不再进行更多尝试。

请求

struct Request {

    var modifiers: [RequestModifier] = []

    var URLRequest: URLRequest

    var callback: (Response<Data>) -> Void

    var cancellable: Cancellable

    var retried = 0

    var retryClosure: (Request, Int, DispatchTimeInterval, () -> Void) -> Void

    func retry(max: Int = Int.max, delay: DispatchTimeInterval = .seconds(0), failCallback: () -> Void = {})
}

RequestNSURLRequest 的包装器。它提供了来自 NSURLRequest 的所有方法的代理。您可以在 RequestEnhancer (参见RequestEnhancer) 和 Response 中访问 Request。您可以在 RequestEnhancer 中随意修改 RequestcancellableFetcher.request 返回的相同的 Cancellable 相同,这是创建这个 Request 的请求。 retried 计算调用了多少次 retryretryClosure 代表 retry 的实现。

响应

struct Response<T> {

    public let result: FetcherResult<T>
    public let rawResponse: HTTPURLResponse?
    public let rawData: Data?
    public let request: Request
}

响应表示请求的结果(服务器对它的响应)。在fetcher.request的每个回调中,您都会得到一个响应实例。声明

TEndpoint中的OUT具有相同的类型。FetcherResult<T>Result<T, FetcherError>的类型别名。

还有一些扩展

extension Response {

    func map<U>(_ transform: (T) -> U) -> Response<U>

    func flatMap<U>(_ transform: (T) -> FetcherResult<U>) -> Response<U>

    var rawString: String?
}

mapflatMapResult中的方式相同(并且仅应用于result)。rawString返回使用响应头中指定的字符编码解码的rawData

端点

Endpoint是一个类,它定义了请求的URL(path)、使用的InputEncoding、特定于请求的RequestModifier以及请求的HTTP方法。其实现有以下构造函数。

init(_ path: String, modifiers: [RequestModifier])

init(_ path: String, inputEncoding: InputEncoding, modifiers: [RequestModifier])

init(_ path: String, modifiers: RequestModifier...)

init(_ path: String, inputEncoding: InputEncoding, modifiers: RequestModifier...)

通常您不会直接使用Endpoint或创建其子类。请参阅方法

端点提供者

EndpointProvider是一个协议,支持创建可重用端点的模式。它提供了一个签名与端点相同的静态方法create,但根据上下文创建特定的实现。该模式如下所示

struct Endpoints: EndpointProvider {

    static func get(id: String, text: String) -> GET<Void, ExampleObject> {
        return create("get", modifiers: URLQueryItem(name: "id", value: "\(id)"), URLQueryItem(name: "text", value: "\(text)"))
    }

    static func post() -> POST<ExampleObject, Void> {
        return create("post")
    }
}

请注意,create可以替换为GETPOST,但我们认为只在一个地方指定类型更好。另一个优点是,您可以为每个Endpoint添加隐式RequestModifier(请参阅RequestModifier)。为此,请实现implicitModifiers

struct Endpoints: EndpointProvider {

    static var implicitModifiers: [RequestModifier] = [StatusCodeResponseVerifier(code: 200)]

    static func post() -> POST<ExampleObject, Void> {
        return create("post")
    }
}

还可以将端点声明为属性

struct Endpoints: EndpointProvider {

    static var post = POST<ExampleObject, Void>("post")
}

这仅在没有直接传递任何参数到URL时可以进行。在本例中,不会工作。如果需要,可以像这样修复

struct Endpoints: EndpointProvider {

    static var post: POST<ExampleObject, Void> = create("post")
}

方法

这些都是预定义的Endpoint实现,代表了某些HTTP方法

  • 连接
  • 删除
  • 获取
  • 标题
  • 选项
  • 修补程序
  • POST
  • PUT
  • TRACE

请求增强器

protocol RequestEnhancer {

    static var priority: RequestEnhancerPriority { get }

    var instancePriority: RequestEnhancerPriority? { get }

    func enhance(request: inout Request)

    func deenhance(response: inout Response<SupportedType>)
}

请求增强器是一种修改Fetcher行为协议。

增强在输入数据被编码到其中(请参见输入编码)之后并在RequestPerformer(请参见请求执行器)执行之前应用于每个Request。您可以根据自己的意愿修改Request。默认实现不做任何事情。

去增强Request相同,但用于传入的Response。它在输出数据解码(请参见输入编码)后和请求回调之前被调用。默认实现不做任何事情。

优先级用于决定多个RequestEnhancer实例应该按照什么顺序应用。默认值是.normal

instancePriority解决了每个RequestEnhancer实例可能有不同优先级的问题(priority是静态的)。默认值是空值,在这种情况下使用priority。如果instancePriority不为空值,则使用它替代priority

可以通过register方法(请参见Fetcher)将RequestEnhancer添加到Fetcher中。

请求增强器优先级

enum RequestEnhancerPriority {

    case low  
    case normal
    case high
    case fetcher
    case max
    case custom(value: Int)
}

RequestEnhancerPriority表示RequestEnhancer的优先级。顺序是从最低到最高优先级。.normal是默认优先级。.fetcher用于内部RequestEnhacer,不应由其他程序使用。.max仅当您需要先于内部RequestEnhancer时使用。建议在必要的情况下仅使用.custom

extension RequestEnhancerPriority {

    var less: RequestEnhancerPriority

    var more: RequestEnhancerPriority
}

这些修改器允许您指定一个RequestEnhancer应该在另一个之前运行(这是一个方便的方式来指定顺序,而无需关心顺序或不相关的增强器)。它们也是priority之所以是静态的的原因。在这个例子中,始终运行RequestEnhancer2RequestEnhancer1之前。

struct RequestEnhancer1: RequestEnhancer {

	func enhance(request: inout Request) {
		...
	}
}

struct RequestEnhancer2: RequestEnhancer {

	static let priority: RequestEnhancerPriority = RequestEnhancer1.priority.more

	func enhance(request: inout Request) {
		...
	}
}

请求修饰器

请求增强器用于所有由Fetcher实例发出的请求。RequestModifier是一种可添加到每个单独请求的标记协议。然后RequestEnhancer可以查看Request.modifiers以查看是否存在实例并据此采取行动。例如,RequestLogger的行为由RequestLogging指定,它是一个包含一些额外数据的RequestModifier

向请求添加RequestModifier有三种方法

  1. register方法在Fetcher中(参见Fetcher)。RequestModifier应用于所有请求。
  2. EndpointProvider中的implicitModifiers(参见EndpointProvider)。RequestModifier应用于使用由EndpointProvidercreate方法创建的Endpoint所创建的所有请求。
  3. Endpointinit中的modifiers参数(参见Endpoint)。RequestModifier仅应用于使用此特定Endpoint实例的请求。

实现

BaseUrl

BaseUrlRequestModifier,它告诉BaseUrlRequestEnhancer在由Endpoint指定的URL之前插入哪个URL。BaseUrlRequestEnhancer默认已注册。BaseUrl使用RequestEnhancerPriority决定如果注册了多个,哪一个将使用。initbaseUrl参数指定要插入的URL。如果它是nil,则不执行任何操作。有一个特殊的实例BaseUrl.Ignore,它可以抑制其他BaseUrl

示例可以在快速概述中的最后一个示例中找到。

RequestLogger

RequestLogger是调试请求的好工具。默认情况下,它会将有关请求(和响应)的信息记录到控制台,尽管可以使用init中的logFunction来覆盖它。要记录的内容使用RequestLoggingRequestModifier)指定。RequestLoggingOptionSet,可以通过将它们放入数组来选择多个选项。如果没有RequestLogging的实例,则使用init中的defaultOptions。以下是RequestLogging的选项:

  • 请求URL
  • 时间
  • 响应代码
  • 请求头
  • 请求体
  • 响应头
  • 响应体
  • all - 上面的所有内容
  • disabled - 没有任何内容

defaultOptionsrequestUrlresponseCodetime组成。

ResponseVerifier

protocol ResponseVerifier: RequestModifier {

    func verify(response: Response<SupportedType>) -> FetcherError?
}

verifyResponseVerifierEnhancer(默认已注册)调用。ResponseVerifierEnhancer获取所有ResponseVerifier的实例并调用verify方法。第一个非nil的结果用于通过Response.flatMap修改响应。只有当Response.result为成功时才执行动作。

StatusCodeResponseVerifier是一个预实现的ResponseVerifier,它将响应状态码与从init中获得的代码进行比较。如果没有找到匹配项,则返回.invalidStatusCode,否则返回nil。

输入编码

InputEncoding是一个标记协议,说明了应该如何将数据编码到Request和从Response解码。每个Endpoint都有一个默认编码,基于它所使用的方法(例如,GET将数据编码到URL,而POST则编码到请求体等),但可以在init中通过传递不同的编码来改变它(参见Endpoint)。

InputEncodingWithEncoder

protocol InputEncodingWithEncoder: InputEncoding {

    func encode(input: SupportedType, to request: inout Request)
}

InputEncodingWithEncoderInputEncoding的扩展。通常DataEncoder负责根据InputEncoding(参见DataEncoder)来编码输入数据。使用该协议时,会调用encode方法来完成工作。

StandardInputEncoding

enum StandardInputEncoding: InputEncoding {

    case queryString
    case httpBody
}

StandardInputEncoding表示所有DataEncoder都理解的两种编码。

.queryString将输入数据编码为请求URL(例如http://url.xxx?param1=value)。如在快速概述中的示例所示,这也可以手动完成。.queryString的缺点是输入数据必须是字典。

.httpBody简单地将数据发送到请求体中。编码取决于DataEncoder实现(可能为JSON、XML等)。

RequestPerformer

protocol RequestPerformer {

    var dataEncoder: DataEncoder { get }

    func perform(request: Request, callback: @escaping (Response<Data>) -> Void) -> Cancellable
}

RequestPerformer的职责是与服务器通信(执行请求)。这是通过perform方法完成的,该方法接受Request并带着Response调用callback(这个调用可能不会立即发生)。它返回Cancellable(取消请求的方式)。

RequestPerformer还指定了DataEncoder,这将用于编码和解码数据。

DataEncoder

protocol DataEncoder {

    func encodeToQueryString(input: SupportedType, to request: inout Request)

    func encodeToHttpBody(input: SupportedType, to request: inout Request)

    func encodeCustom(input: SupportedType, to request: inout Request, inputEncoding: InputEncoding)

    func decode(response: Response<Data>) -> Response<SupportedType>
}

DataEncoderFetcher调用它时,用于将输入数据编码到Request,并从Response解码数据。

StandardInputEncoding指定使用哪个encodeToQueryStringencodeToHttpBody

如果使用自定义的InputEncoding实现,且不是InputEncodingWithEncoder,则会调用encodeCustom。默认情况下,调用此方法将导致崩溃(InputEncoding未知)。

实现

AlamofireRequestPerformer

AlamofireRequestPerformer 是基于 Alamofire 的实现。默认情况下它使用 AlamofireJsonDataEncoder(JSON 编码器)作为 DataEncoder,但可以在它的 init 中进行更改。

AlamofireJsonDataEncoder 了解一种特殊的 InputEncoding 类型,即 FormInputEncoding,它支持内容类型 application/x-www-form-urlencoded

请求头

protocol Header: RequestModifier {

    var name: String { get }
    var value: String { get }
}

HeaderRequestModifier 的一种特殊类型,它代表请求的头部。注册的 Header 实例通过内部 RequestEnhancer 添加到 Request 中。

预定义的头部

Headers 收集所有预定义的头部。它们通过扩展添加为嵌套的结构体。例如,这是 Accept 的实现

extension Headers {

    struct Accept: Header {

        let name = "Accept"

        let value: String

        init(value: String) {
            self.value = value
        }
    }
}

extension Headers.Accept {

    static let applicationJson = Headers.Accept(value: "application/json")
    static let textPlain = Headers.Accept(value: "text/plain")
}

其他头部也可以用同样的方式添加(以及特定头部的值)。

目前有这些头部: AcceptContentTypeCharset

Custom 是一个特殊的头部类型。如果某些头部只需要在单个地方使用,则可以使用它,不需要创建一个新的结构体。

FetcherError

enum FetcherError: Error {
    case requestError(Error)
    case invalidStatusCode
    case nilValue
    case custom(Error)
    case unknown
}

FetcherError 用作 Response.result 中的错误类型。

ErrorHandler

protocol ErrorHandler {

    func canResolveError(response: Response<SupportedType>) -> Bool

    func resolveError(response: Response<SupportedType>, callback: (Response<SupportedType>) -> Void)
}

ErrorHandler 告诉 Fetcher 如果 Response.result 失败时应该做什么。它可以在其 init 中通过名为 errorHandler 的参数设置。默认的是 NoErrorHandler

在解决错误时,Fetcher 首先调用 canResolveError。如果它返回 false,则行为没有变化,并且使用未更改的 Response 调用回调。如果返回 true,则调用 resolveError 而不是回调。然而,回调被传递为一个闭包,您可以用修改后的 Response 调用它。

BaseStatusCodeErrorHandler

BaseStatusCodeErrorHandler 是一个实现用于根据响应的状态码解决错误的“抽象”类。它具有与 StatusCodeResponseVerifier 类似的 init 方法。canResolveError 如果状态码来自 init 中的代码,则返回 true

CompositeErrorHandler

struct CompositeErrorHandler: ErrorHandler {

    init(handlers: [ErrorHandler])

    init(handlers: ErrorHandler...)
}

CompositeErrorHandler 允许多个 ErrorHandler 的组合。仅使用能够解决错误的第一个 ErrorHandler 实际解决错误。

实现

NoErrorHandler

NoErrorHandler 不做任何事(canResolveError 总返回 false)。

NoInternetErrorHandler

final class NoInternetErrorHandler: BaseStatusCodeErrorHandler {

    init(maxRepetitions: Int = 3, delay: DispatchTimeInterval = .seconds(1))
}

NoInternetErrorHandler 通过调用 retry(见 retry)处理状态码 599。可以在 init 中设置 retry 的参数。

RequestTimeOutErrorHandler

final class RequestTimeOutErrorHandler: BaseStatusCodeErrorHandler {

    init(maxRepetitions: Int = 3, delay: DispatchTimeInterval = .seconds(0))
}

RequestTimeOutErrorHandler 通过调用 retry(见 retry)处理状态码 408。可以在 init 中设置 retry 的参数。

RxFetcher

您可以用 Observable 以更“响应式”的方式发出请求,而不使用回调。为此,您需要一个 RxFetcher 实例,该实例可以通过 Fetcherrx 属性获取。与原始方法一样,RxFetcher 提供了相同的 request 方法,但它们返回 Observable。修改了快速概览中的第三个示例。

let fetcher = Fetcher(requestPerformer: AlamofireRequestPerformer())
fetcher.register(requestEnhancers: HttpBinResponseTranslation())
fetcher.register(requestModifiers: BaseUrl(baseUrl: "https://httpbin.org"))

fetcher.rx.request(Endpoints.get(id: "1", text: "a").subscribe(onNext: { response in
    switch response.result {
    case .success(let value):
        // value was already transformed with DataMapper.
        print(value)
    case .failure(let error):
        // Handle error
        break
    }
}).addDisposableTo(...)

可观测扩展

extension ObservableConvertibleType where E: ResponseProtocol {

    func retryRequest(max: Int = Int.max, delay: DispatchTimeInterval = .seconds(0)) -> Observable<E>

    func asResult() -> Observable<FetcherResult<E.T>>
}

retryRequestRequestretry的响应式变体(参见retry)。

asResult返回一个映射的序列,包含Response.result

线程安全

Fetcher默认在后台线程执行许多操作,因此需要是线程安全的。这也适用于像RequestPerformerRequestEnhancerErrorHandler等对象。通常,这些协议的所有方法都是作为纯函数实现的,因此不会引起任何问题。另请参阅DataMapper#ThreadSafety

版本控制

此库使用语义版本控制。在版本1.0之前,即使是次要版本也可能出现API重大更改。我们考虑版本0.1为预发布版本,这意味着API应该稳定,但尚未在实际项目中测试。在测试后,我们将进行必要的调整,并将版本提升到1.0(第一次发布)。

作者

使用的库

AlamofireRequestPerformer

RxFetcher

Tests

许可协议

Fetcher可在MIT 许可协议下使用。