Forest 客户端
Forest 客户端是一个基于 URLSession
和 URLSessionTask
的灵活可扩展的 RESTful API 客户端框架。它已经包含了从 JSON 到最常用数据类型的网络对象映射器。由于其简单的数据编码/解码方法和可扩展的架构,您可以轻松添加自定义的网络对象映射器。Forest 提供构建健壮的后端应用程序客户端所需的所有功能。
您可能会问为什么不使用其他任何经过验证的网络框架?当然,您会得到一个最适合您需求和风格偏好的框架,但总是有一个选择。以下是我在 Forest 客户端中实现了的高级网络层功能的列表
- 声明性请求和响应配置
- 无需透明的完全可定制的响应拦截和重放/重试,允许异步处理。主要映射错误和在 OAuth 类似安全请求中捕获过期的令牌
- 可扩展性。通过关闭、协议或子类化添加自定义响应映射器和体数据编码器,以方便特定需求。使用默认框架类、扩展它们或子类化,应该很简单。
- 在需要的地方下载并保留文件
- 解析 JSON 主体。将 JSON 映射到数组、字典,或使用遵循
Decodable
协议的对象 - Protobufs 消息的序列化和反序列化(通过 HTTP 的 gRPC)
- 支持文件表单数据作为附加功能
安装
CocoaPods
pod 'Forest'
添加支持 Protobufs 的扩展
pod 'Forest/Protobuf'
使用可达性服务
pod 'Forest/Reachability'
别忘了导入框架
import Forest
手动
只需将 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 occurred: \(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()
作者
Natan Zalkin [email protected]
许可证
Forest 根据 MIT 许可证提供。有关更多信息,请参阅 LICENSE 文件。