Condulet
Condulet 是一个基于 URLSession
和 URLSessionTask
的灵活且可扩展的 REST API 客户端构建框架。它已内置了从 JSON 到最常用数据类型的网络对象映射器。由于其简单直观的数据编码/解码方法和可扩展的架构,你可以轻松地添加自定义网络对象映射器。Condulet 为构建强大的后端服务客户端提供了所需的所有功能。
你可能想知道为什么不用其他经过验证的网络框架?当然,你会得到一个最适合你需求和风格偏好的框架,但总是有选择的余地。以下是我对高层次网络层的期望以及 Condulet 中的实现:
- 声明式请求和响应配置
- 透明且完全可自定义的响应拦截和重做/重试,允许异步处理。主要用于处理错误和捕获以 OAuth 类似方式保护请求的到期令牌
- 可扩展性。使用闭包、协议或子类轻松添加自定义响应映射器和正文数据编码器,以满足特定需求。可以使用默认框架的类或扩展它们或子类,但应该简单易行。
- 下载并在需要的地方保留文件
- JSON 正文的反序列化。将 JSON 映射到数组、字典或使用符合
Decodable
协议的对象 - 支持 Protobuf 消息的序列化和反序列化(通过 HTTP 的 gRPC)
- 作为附加功能支持表单数据
安装
CocoaPods
pod 'Condulet'
添加支持扩展的 Protobufs
pod 'Condulet/Protobuf'
使用可达性服务
pod 'Condulet/Reachability'
并不要忘记导入框架
import Condulet
手动操作
只需将来自Core
和Protobuf
目录的文件放到你的项目中某处。要使用 Protobuf 扩展,你还需要将 SwiftProtobuf 框架集成到你的项目中。
用法
处理网络任务的核心类是ServiceTask
。ServiceTask
包括工厂方法,有助于配置请求和响应参数和处理程序。如果您需要更多控制请求过程和响应处理,可以使用委托并实现ServiceTaskRetrofitting
协议,通过 retrofitter 修改任务行为。您还可以从ServiceTask
派生,它为此而设计。
发送一个期望 JSON 响应的 GET 请求
ServiceTask()
.url("https://host.com/path/to/endpoint")
.method(.GET)
.query(["param": value])
// Expecting valid JSON response
.json { (object, response) in
print("JSON response received: \(object)")
}
.error { (error, response) in
print("Error occured: \(error)")
}
.perform()
发送和接收数据
使用符合Codable协议的对象发送和接收数据
struct NameRequest: Encodable {
let name: String
}
struct NameResponse: Decodable {
let isValid: Bool
}
ServiceTask()
// Set base url and HTTP method
.endpoint(.POST, "https://host.com")
// Add path to resource
.path("/path/to/resource")
// Serialize our Codable struct and set body
.body(codable: NameRequest("some"))
// Expect response with the object of 'NameResponse' type
.codable { (object: NameResponse, response) in
print("Name valid: \(object.isValid)")
}
// Otherwise will fail with error
.error { (error, response) in
print("Error occured: \(error)")
}
.perform()
只是下载一些文件
ServiceTask()
.headers(["Authorization": "Bearer \(token)"])
.method(.PUT)
.url("https://host.com/file/12345")
.body(text: "123456789")
.file { (url, response) in
print("Downloaded: \(url)")
// Remove temp file
try? FileManager.default.removeItem(at: url)
}
.error { (error, response) in
print("Error occured: \(error)")
}
// When download destination not provided, content will be downloaded and saved to temp file
.download()
上传多部分表单数据编码的内容
do {
// Create new form data builder
var formDataBuilder = FormDataBuilder()
// Filename and MIME type will be obtained automatically from URL. It can be provided explicitly too
formDataBuilder.append(.file(name: "image", url: *url*))
// Generate form data in memory. It also can be written directly to disk or stream using encode(to:) method
let formData = try formDataBuilder.encode()
ServiceTask()
.endpoint(.POST, "https://host.com/upload")
.body(data: formData, contentType: formDataBuilder.contentType)
.response(content: { (response) in
switch response {
case .success:
print("Done!")
case .failure(let error):
print("Failed to upload: \(error)")
}
})
.perform()
}
catch {
print("\(error))
return
}
发送和接收 Protobuf 消息(通过 HTTP 的 gRPC)
ServiceTask()
.endpoint(.POST, "https://host.com")
// Create and configure request message in place
.body { (message: inout Google_Protobuf_StringValue) in
message.value = "something"
}
// Expecting Google_Protobuf_Empty message response
.proto{ (message: Google_Protobuf_Empty, response) in
print("Done!")
}
.error { (error, response) in
print("Error occured: \(error)")
}
.perform()
// Or another version of the code above with explicitly provided types
ServiceTask()
.endpoint(.POST, "https://host.com")
// Create and configure request message in place
.body(proto: Google_Protobuf_SourceContext.self) { (message) in
message.fileName = "file.name"
}
// Expecting Google_Protobuf_Empty message response
.response(proto: Google_Protobuf_Empty.self) { (response) in
switch response {
case .success(let message):
print("Done!")
case .failure(let error):
print("Error occured: \(error)")
}
}
.perform()
作者
纳坦·扎尔金 [email protected]
许可证
Condulet 可在 MIT 许可证下获得。更多信息请参阅 LICENSE 文件。