通过在 iOS、macOS 和 Linux 上声明 Web 服务和端点,以清晰且类型安全的方式发出 HTTP 请求
在 Swift 中发出 URL 请求时,您主要有两种选择:使用 Foundation 中的 URLSession API,或使用一些重量级的框架。
该框架旨在轻量级,同时保持可定制性,并专注于以声明方式声明 API 接口。声明后,向各种端点发出请求非常简单且类型安全。它适用于 iOS、macOS 和 Linux。
Andrew 通过实现许多不同的用 Swift 编写的应用程序和后端服务开发了这种策略。 他使用这种范例在他的前端和后端(都用 Swift 实现)之间以及与 Spotify、FreshDesk、Stripe 等服务进行通信。
我们提供了一个单独的存储库 DecreeServices,其中包含流行服务的服务声明
四种类型的端点
这些 协议 声明端点是否具有输入和/或输出。
EmptyEndpoint
(无输入或输出)InEndpoint
(仅输入)OutEndpoint
(仅输出)InOutEndpoint
(输入和输出)五种输入格式
这些 格式 用于使用 Swift Encodable 协议对端点的输入进行编码。
两种输出格式
这些 格式 用于使用 Swift Decodable 协议初始化端点的输出。
三种类型的授权
允许设置用于 Web 服务中所有端点的授权。 然后,每个端点都可以指定授权要求。
高级功能
可配置
您可以选择执行高级配置以处理请求和响应。
几乎 100% 的代码覆盖率
我们的大部分代码都经过单元测试覆盖,以确保可靠性。
请求和响应日志记录
全面的错误报告
Decree 抛出和返回的错误旨在用户友好,同时还公开详细的诊断信息。
Mocking
允许模拟端点响应,以便轻松进行自动化测试。
第三方服务
我们创建了一个单独的框架,用于定义多个第三方服务的服务和端点。 请在DecreeServices上查看它。
以下是此框架的一些使用示例。
在这里,我们定义了一个 CheckStatus
端点,它是一个 GET(默认值),在路径“/status”上,没有输入或输出。 向下滚动以查看 ExampleService 的定义。
struct CheckStatus: EmptyEndpoint {
typealias Service = ExampleService
let path = "status"
}
然后,我们可以使用该定义来发出异步请求。
CheckStatus().makeRequest() { result in
switch result {
case .success:
print("Success :)")
case .failure(let error):
print("Error :( \(error)")
}
}
我们还可以发出同步请求,如果发生错误,该请求只会抛出错误。
try CheckStatus().makeSynchronousRequest()
我们还可以定义具有输入和/或输出的端点。 在这里,我们定义了一个 Login 端点,它是对“/login”的 POST 请求,用户名和密码参数编码为 JSON。 如果成功,则端点应返回一个令牌。
struct Login: InOutEndpoint {
typealias Service = ExampleService
static let method = Method.post
let path = "login"
struct Input: Encodable {
let username: String
let password: String
}
struct Output: Decodable {
let token: String
}
}
然后我们可以发出异步请求。
Login().makeRequest(with: .init(username: "username", password: "secret")) { result in
switch result {
case .success(let output):
print("Token: \(output.token)")
case .failure(let error):
print("Error :( \(error)")
}
}
或者我们可以发出同步请求,如果成功则返回输出,否则抛出异常。
let token = try Login().makeSynchronousRequest(with: .init(username: "username", password: "secret")).token
对于具有较大输出的端点,我们可以将它们直接下载到文件,而不是将整个响应保存在内存中。
struct GetDocument: OutEndpoint {
typealias Service = ExampleService
typealias Output = Data
let id: Int
var path: String {
return "documents/\(id)"
}
}
GetDocument(id: 42).makeDownloadRequest() { result in
switch result {
case .success(let url):
// open or move the url
case .failure(let error):
print("Error :( \(error)")
}
}
请注意,您可以在任何具有输出的端点上使用 makeDownloadRequest
方法(无论其格式如何),但是通常使用原始数据输出最有意义。
要使以上示例起作用,唯一需要的额外代码是定义 ExampleService
struct ExampleService: WebService {
// There is no service wide standard response format
typealias BasicResponse = NoBasicResponse
// Errors will be in the format {"message": "<reason>"}
struct ErrorResponse: AnyErrorResponse {
let message: String
}
// Requests should use this service instance by default
static var shared = ExampleService()
// All requests will be sent to their endpoint at "https://example.com"
let baseURL = URL(string: "https://example.com")!
}
在这里,我们定义了一个名为 ExampleService
的 WebService
,其中包含一些属性。
这就是您所需要的。 然后,您可以根据需要定义任意数量的端点,并以清晰且类型安全的方式使用它们。
要查看真实世界的示例,请查看我们如何在 DecreeServices 中声明服务。
我们希望实现的功能已添加为带有 增强标签的问题。 如果您有任何功能请求,请随时创建一个新问题。
非常鼓励您报告任何问题和/或为新功能提出 pull 请求。