Fetch 是一个基于资源的网络抽象层,基于 Alamofire。
首先使用 Config
设置 APIClient
。只需要一行代码。在 application(_:didFinishLaunchingWithOptions:)
函数中调用它。
APIClient.shared.setup(with: Config(baseURL: URL(string: "https://api.github.com")!))
让我们创建一个名为 Organization
的结构体,包含一些属性。模型将从网络响应中解析。
struct Organization: Decodable, Equatable {
let id: Int
let name: String
let location: String
}
一个 Resource
包含创建网络请求和解析响应所需的所有必要信息。
let resource = Resource<Organization>(
method: .get,
path: "/orgs/allaboutapps")
发送请求并将响应解析为类型化的模型 Organisation
。
resource.request { (result) in
switch result {
case .success(let networkResponse):
print("Status code:", networkResponse.urlResponse.statusCode)
print("Model:", networkResponse.model)
case .failure(let apiError):
print("Error:", apiError)
}
}
默认情况下,配置使用标准库提供的 JSONDecoder 和 JSONEncoder,但不限于此。这两种类型都已扩展为符合 ResourceDecoderProtocol 和 ResourceEncoderProtocol,允许您定义自己的自定义解码器/编码器。 Resource 提供了解码和编码闭包,这些闭包使用配置中定义的解码器和编码器。 如果要为资源实现不同的行为,可以在创建资源时提供一个闭包。
有效负载解包
有时内容会被打包在一个信封中,这使得解析变得困难。 在这种情况下,您可以定义所谓的“根键”。 根键定义了信封中要解析的内容的路径。 这意味着只会解析用根键定义的内容。
示例
这是一个应该被解析的响应。
{
"data": {
"people": [
{
"name": "Alex"
},
{
"name": "Jeff"
},
{
"name": "Tom"
},
{
"name": "Xavier"
}
]
}
}
我们只想要 people,它是一个 Person 的数组。 而不是定义一个对层次结构建模的结构,我们在资源上定义 "root keys" 以仅获取数组。
struct Person: Decodable {
let name: String
}
let resource = Resource<[Person]>(
path: "/people",
rootKeys: ["data", "people"]
)
resource.request { result in
...
}
Fetch 为您提供了一套通用的模拟方法。 要执行模拟,必须启用 APIClients Config 上的 shouldStub
并且必须为资源注册一个模拟。
使用 json 响应模拟成功的网络请求
let stub = StubResponse(statusCode: 200, fileName: "success.json", delay: 2.0)
let resource = Resource<Person>(path: "/test")
APIClient.shared.stubProvider.register(stub: stub, for: resource)
上面的模拟将返回一个 200 状态代码,内容来自从您应用程序的 bundle 加载的 success json 文件,并将延迟两秒。
模拟未经授权的错误
let stub = StubResponse(statusCode: 401, fileName: "unauthorized.json", delay: 2.0)
let resource = Resource<Person>(path: "/unauthorized")
APIClient.shared.stubProvider.register(stub: stub, for: resource)
模拟不限于 json,您还可以提供原始数据或提供符合 Encodable 协议的实例。
使用 Encodable 进行模拟
struct Person: Encodable {
let name: String
let age: Int
}
let peter = Person(name: "Peter", age: 18)
let stub = StubResponse(statusCode: 200, encodable: peter, delay: 2.0)
let resource = Resource<Person>(path: "/peter")
APIClient.shared.stubProvider.register(stub: stub, for: resource)
交替模拟
let successStub = StubResponse(statusCode: 200, fileName: "success.json", delay: 0.0)
let failureStub = StubResponse(statusCode: 404, fileName: "notFound.json", delay: 0.0)
let alternatingStub = AlternatingStub(stubs: [successStub, failureStub])
let resource = Resource<Person>(path: "/peter")
APIClient.shared.stubProvider.register(stub: alternatingStub, for: resource)
每次执行资源时,它将遍历给定的模拟,并且总是返回一个与之前不同的模拟。
随机模拟
RandomStub
的工作方式类似于 AlternatingStub
,但总是从数组中返回一个随机的模拟。
条件模拟
基于特定条件模拟行为可以使用条件模拟来实现。
示例
模拟一个受用户授权保护的端点,并根据您应用程序的授权状态返回成功或错误
let conditionalStub = ClosureStub { () -> Stub in
let unauthorizedStub = StubResponse(statusCode: 401, data: Data(), delay: 2)
let okStub = StubResponse(statusCode: 200, data: Data(), delay: 2)
return CredentialsController.shared.currentCredentials == nil ? unauthorizedStub : okStub
}
let resource = Resource(path: "/auth/secret")
APIClient.shared.stubProvider.register(stub: conditionalStub, for: resource)
自定义模拟
您可以通过符合 Stub 协议来创建自定义模拟。
struct CustomStub: Stub {
...
}
自定义 StubProvider
您可以通过符合 StubProvider 协议来创建自定义 StubProvider
。
struct CustomStubProvider: StubProvider {
...
}
使用自定义 stubProvider 初始化/设置 APIClient
let client = APIClient(config: Config(stubProvider: customStubProvider))
APIClient.shared.setup(with: Config(stubProvider: customStubProvider))
或者,替换 APIClient 上的默认 StubProvider
APIClient.shared.setStubProvider(customStubProvider)
实现了以下缓存类型
设置缓存
let cache = MemoryCache(defaultExpiration: .seconds(3600))
let config = Config(
baseURL: URL(string: "https://example.com")!,
cache: cache,
cachePolicy: .networkOnlyUpdateCache)
let client = APIClient(config: config)
注意:要使用缓存,您从资源加载的模型必须符合 Cacheable 协议。
混合缓存
混合缓存允许您组合两个单独的缓存,使用的缓存类型不受限制。
自定义缓存实现
要实现自定义缓存,您必须创建一个符合 Cache 协议的类/结构体。
class SpecialCache: Cache {
...
}
缓存策略
缓存策略定义了资源的加载行为。 您可以在创建资源时直接在资源上设置策略,可以在 APIClient 的配置中设置策略,也可以将其作为参数传递给资源的 fetch 函数。
注意:资源中定义的策略始终优先于配置中定义的策略。
从缓存加载,否则从网络加载
这将首先尝试从缓存中读取请求的数据,如果数据不可用或已过期,则将从网络加载数据。
let resource: Resource<X> = ...
resource.fetch(cachePolicy: .cacheFirstNetworkIfNotFoundOrExpired) { (result, finishedLoading) in
...
}
从网络加载并更新缓存
这将从网络加载数据并更新缓存。完成闭包只会使用来自网络的值调用。
let resource: Resource<Person> = ...
resource.fetch(cachePolicy: .networkOnlyUpdateCache) { (result, finishedLoading) in
...
}
从缓存加载数据并始终从网络加载数据
这将从缓存加载数据并从网络加载数据。您将异步地在完成闭包中获得这两个值。
let resource: Resource<Person> = ...
resource.fetch(cachePolicy: .cacheFirstNetworkAlways) { (result, finishedLoading) in
...
}
有关策略的概述,请查看 Cache.swift 中的实现
使用 Xcode 11+:转到 Project > Swift Packages > +
并输入 git@github.com:allaboutapps/Fetch.git
或者手动更新您的 Package.swift 文件
dependencies: [
.package(url: "git@github.com:allaboutapps/Fetch.git", from: "1.0.9"),
....
],
targets: [
.target(name: "YourApp", dependencies: ["Fetch"]),
]