想象一下这样的世界:网络请求听从你的指挥,每次调用都像一条训练有素的宠物,被结实的Leash
牵引着。欢迎来到一个从重复的APIManager
或Alamofire+ProjectName
中解脱出来,如同在公园里悠闲漫步的地方。有了Leash
,你不仅仅是构建一个网络层,你还在精心编排一场拦截器的交响乐,它们巧妙地以精确和优雅的方式引导你的请求。
渴望了解更多细节吗?拦截器部分是您掌握它的门户。 除了命令和控制之外,Leash
还将编码、解码和无缝身份验证的技巧带到您的指尖,同时与熟悉的 Alamofire 的旋律相协调。 通过Leash
拥抱网络复杂性的艺术 - 在这里,您代码的潜力是无限的。
要使用 Swift Package Manager 将 Leash
集成到您的项目中,请在您的 Package.swift
中指定它
// swift-tools-version:5.0
import PackageDescription
let package = Package(
name: "YourPackageName",
dependencies: [
.package(
url: "https://github.com/LucianoPolit/Leash.git",
.upToNextMajor(from: "3.2.0")
)
],
targets: [
.target(
name: "YourTargetName",
dependencies: [
"Leash",
"LeashInterceptors",
"RxLeash"
]
)
]
)
要使用 CocoaPods 将 Leash
集成到您的项目中,请在您的 Podfile
中指定它
pod 'Leash', '~> 3.2'
pod 'Leash/Interceptors', '~> 3.2'
pod 'Leash/RxSwift', '~> 3.2'
要使用 Carthage 将 Leash
集成到您的项目中,请在您的 Cartfile
中指定它
github "LucianoPolit/Leash" ~> 3.2
步骤 1:配置管理器
首先设置一个 Manager
。您可以详细配置它,或者使用快捷方法。请参阅所有可配置的选项。配置示例
详细设置
let manager = Manager.Builder()
.scheme("http") // Define the scheme.
.host("localhost") // Specify the host.
.port(8080) // Set the port.
.path("api") // Define the base path.
.build() // Build the manager.
或者,为了快速设置
let manager = Manager.Builder()
.url("https://:8080/api") // Set the entire URL in one go.
.build() // Build the manager.
步骤 2:创建客户端
接下来,初始化一个 Client
来处理您的请求
let client = Client(
manager: manager
)
步骤 3:执行请求
设置好一个 Endpoint
之后,您就可以执行请求了。例如
client.execute(
APIEndpoint.readAllUsers
) { (response: Response<[User]>) in
// Handle the response here.
}
简化调用
更喜欢更简洁的方法?您可以像这样简化调用
usersClient.readAll { response in
// Handle the response here.
}
这种精简的方法提高了可读性和效率。为了获得最佳实践和清晰的架构,请参考示例项目。
理解参数配置
Leash 提供了多种方式来配置不同类型请求的参数。 您可以使用各种编码,具体取决于您的端点是否需要查询参数或主体参数
查询参数类型:
QueryEncodable
[String: CustomStringConvertible]
主体参数类型:
Encodable
[String: Any]
示例实现
以下是如何实现每种类型
enum APIEndpoint {
case first(QueryEncodable)
case second([String: CustomStringConvertible])
case third(Encodable)
case fourth([String: Any])
}
extension APIEndpoint: Endpoint {
var path: String {
"/it/does/not/matter/"
}
var method: HTTPMethod {
switch self {
case .first: return .get
case .second: return .get
case .third: return .post
case .fourth: return .post
}
}
var parameters: Any? {
switch self {
case let .first(request): return request // This is `QueryEncodable`.
case let .second(request): return request // This is `[String: CustomStringConvertible]`.
case let .third(request): return request // This is `Encodable`.
case let .fourth(request): return request // This is `[String: Any]`.
}
}
}
使用的编码类
Leash
使用不同的编码类
QueryEncodable
和 [String: CustomStringConvertible]
。[String: Any]
。Encodable
。自定义编码
要自定义参数编码,请覆盖 Client.urlRequest(for:)
方法。
如果您想以不同的方式编码参数,则必须覆盖 Client.urlRequest(for:)
方法。
指定响应类型
在使用 Leash
执行请求的过程中,必须定义预期的响应类型,该类型必须符合 Decodable
协议。 此规范确保在收到成功响应后,Leash 会有效地将数据解码为您指定的类型。 这是一个简单的例子来说明这个过程
client.execute(
APIEndpoint.readAllUsers
) { (response: Response<[User]>) in
// On success, `response.value` will be of type `[User]`.
}
使用 JSONDecoder 序列化
Leash 使用 JSONDecoder 进行响应序列化。 要自定义此过程或使用其他序列化程序,请实现您自己的 响应序列化程序,利用 Alamofire
中的 DataResponseSerializerProtocol。
示例:JSON 响应序列化器
extension DataRequest {
@discardableResult
func responseJSON(
client: Client,
endpoint: Endpoint,
completion: @escaping (Response<Any>) -> Void
) -> Self {
response(
client: client,
endpoint: endpoint,
serializer: JSONResponseSerializer(),
completion: completion
)
}
}
扩展 Client 以简化请求执行
extension Client {
@discardableResult
func execute(
_ endpoint: Endpoint,
completion: @escaping (Response<Any>) -> Void
) -> DataRequest? {
do {
return request(for: endpoint)
.responseJSON(
client: self,
endpoint: endpoint,
completion: completion
)
} catch {
completion(
.failure(
Error.encoding(error)
)
)
return nil
}
}
}
使用这些扩展的一个例子可能看起来像这样
client.execute(
APIEndpoint.readAllUsers
) { (response: Response<Any>) in
// On success, `response.value` will be of type `Any`.
}
现在,您可以创建自己的 DataResponseSerializer
并利用 Leash
的所有功能!
您是否需要验证您的请求? 使用 Leash
非常简单。 这是方法
class APIAuthenticator {
var accessToken: String?
}
extension APIAuthenticator: Authenticator {
static var header = "Authorization"
var authentication: String? {
guard let accessToken = accessToken
else { return nil }
return "Bearer \(accessToken)"
}
}
只需像这样注册您的身份验证器
let authenticator = APIAuthenticator()
let manager = Manager.Builder()
{ ... } // Include other necessary configurations here.
.authenticator(authenticator) // Register the authenticator.
.build() // Build the manager.
瞧! 您的请求现在已通过身份验证。 担心令牌过期? 查看 此处 的解决方案!
解锁框架的核心力量
Interceptors
是 Leash
的强大功能,能够在请求生命周期的各个阶段执行操作。 它们分为五种类型,用于精确干预
生命周期集成
每个请求至少经过三种拦截器类型:Execution
、Failure
或 Success
和 Completion
。Serialization
类型根据您是否正在序列化响应来使用。
模块化和异步执行
Manager
可以容纳一个 Interceptors
数组,它们以异步方式按添加的顺序执行。 这种排队可确保有序的顺序处理流程。 如果拦截器请求结束操作,则不会调用同一类型的后续拦截器,从而提供有效的错误处理和流程控制。
独立但有凝聚力
每个拦截器都独立运行,确保没有可能使您的项目结构复杂化的相互依赖性。 这种模块化设计允许轻松删除或添加拦截器,而不会影响其他组件。 此外,它们在不同项目中的可重用性增加了它们的通用性。
集成拦截器
要将拦截器添加到您的 Manager
,请按照此模式
let manager = Manager.Builder()
{ ... } // Include other necessary configurations here.
.add(
interceptor: CustomInterceptor() // Insert your custom interceptor.
)
.build()
增强请求准备
Execution Interceptor 允许进行请求前调整和监控。 两个关键实现证明了它的多功能性
class LoggerInterceptor: ExecutionInterceptor {
func intercept(
chain: InterceptorChain<Data>
) {
defer { chain.proceed() }
guard let request = try? chain.request.convertible.asURLRequest(),
let method = request.httpMethod,
let url = request.url?.absoluteString
else { return }
Logger.shared.logDebug("👉👉👉 \(method) \(url)")
}
}
class CacheInterceptor: ExecutionInterceptor {
let controller = CacheController()
func intercept(
chain: InterceptorChain<Data>
) {
// In this scenario, the cache controller decides whether to complete
// the operation based on predefined policies.
// This allows us to instruct the chain to either finish or continue the operation.
defer { chain.proceed() }
guard let cachedResponse = try? controller.cachedResponse(
for: chain.endpoint
)
else { return }
chain.complete(
with: cachedResponse.data,
finish: cachedResponse.finish
)
}
}
优雅地处理请求错误
当 Alamofire
遇到错误时,Failure Interceptor 会介入。 考虑这个例子
ErrorValidator:一个简单但必不可少的拦截器,用于检查特定的错误条件并相应地修改或处理它们。
class ErrorValidator: FailureInterceptor {
func intercept(
chain: InterceptorChain<Data>,
error: Swift.Error
) {
defer { chain.proceed() }
guard case Error.some = error
else { return }
chain.complete(
with: Error.another
)
}
}
优化响应管理
当 Alamofire 成功检索响应时,Success Interceptor 至关重要,它提供了执行其他验证或处理的机会。 以下是两个说明性示例
class BodyValidator: SuccessInterceptor {
func intercept(
chain: InterceptorChain<Data>,
response: HTTPURLResponse,
data: Data
) {
defer { chain.proceed() }
guard let error = try? chain.client.manager.jsonDecoder.decode(
APIError.self,
from: data
)
else { return }
chain.complete(
with: Error.server(error)
)
}
}
class ResponseValidator: SuccessInterceptor {
func intercept(
chain: InterceptorChain<Data>,
response: HTTPURLResponse,
data: Data
) {
defer { chain.proceed() }
let error: Error
switch response.statusCode {
case 200 ... 299: return
case 401, 403: error = .unauthorized
default: error = .unknown
}
chain.complete(
with: error
)
}
}
完善请求的最后阶段
Completion Interceptor 在提供完成处理程序之前执行。 这是两个例子
class LoggerInterceptor: CompletionInterceptor {
func intercept(
chain: InterceptorChain<Data>,
response: Response<Data>
) {
defer { chain.proceed() }
guard let request = try? chain.request.convertible.asURLRequest(),
let method = request.httpMethod,
let url = request.url?.absoluteString
else { return }
switch response {
case .success:
Logger.shared.logDebug("✔✔✔ \(method) \(url)")
case .failure(let error):
Logger.shared.logDebug("✖✖✖ \(method) \(url)")
Logger.shared.logError(error)
}
}
}
class AuthenticationValidator: CompletionInterceptor {
func intercept(
chain: InterceptorChain<Data>,
response: Response<Data>
) {
guard let error = response.error,
case Error.unauthorized = error,
let authenticator = chain.client.manager.authenticator as? APIAuthenticator
else {
chain.proceed()
return
}
RefreshTokenManager.shared.refreshTokenIfNeeded { authenticated, accessToken in
guard authenticated
else {
chain.complete(
with: Error.unableToAuthenticate
)
return
}
authenticator.accessToken = accessToken
do {
try chain.retry()
} catch {
// Typically, retrying does not throw an error.
// However, for maximum safety and to handle unexpected scenarios,
// we wrap it in a do-catch block.
chain.complete(
with: Error.unableToRetry
)
}
}
}
}
注意:您还可以使用 Alamofire
提供的 Adapter 和 Retrier。
序列化拦截器是请求生命周期中的最后也是可选的阶段,具体取决于您是否正在序列化您的响应或只是处理 Data
。
例如,在继续使用 CacheController
时,以下是如何使用成功序列化的响应来更新缓存
class CacheInterceptor: SerializationInterceptor {
let controller = CacheController()
func intercept<T: DataResponseSerializerProtocol>(
chain: InterceptorChain<T.SerializedObject>,
response: Response<Data>,
result: Result<T.SerializedObject, Swift.Error>,
serializer: T
) {
defer { chain.proceed() }
guard let value = response.value,
(try? result.get()) != nil
else { return }
controller.updateCacheIfNeeded(
for: chain.endpoint,
value: value
)
}
}
对于那些喜欢 RxSwift 的人来说,Leash 已经为您提供了一个专门的扩展。 以下是如何使用 RxSwift 简化您的网络调用
client.rx.execute(
APIEndpoint.readAllUsers,
type: [User].self
).subscribe { event in
// Handle the response event here.
}
寻求更简洁的方法? 您可以进一步简化它
usersClient.rx.readAll().subscribe { event in
// Handle the response event here.
}
这种方法与保持您的项目简单和简洁的精神完全一致。 为了深入了解和最佳实践,请探索 示例项目 中概述的结构。
Luciano Polit, lucianopolit@gmail.com
Leash
在 MIT 许可下可用。 有关更多信息,请参阅 LICENSE 文件。