超空间 (Hyperspace) 为 URLSession 和 HTTP 提供了一个简单的抽象层。 主要目标有以下几个:
HTTP
定义),但我们的主要目标是在不进行过度设计的情况下,保持库的高度功能性和可维护性。URLRequest
的一个薄封装,利用了 HTTP
中的定义。TransportSession
(默认为 URLSession
) 来执行 URLRequests
。 处理原始 HTTP
和 Data
。TransportService
来执行 Requests
。 将从 TransportService
返回的原始 Data
转换为 Request
定义的响应模型类型。 这是您的应用程序将直接处理的主要工作对象。创建请求时有多种选择。 这些包括创建静态函数以减少创建 Request
对象时的样板代码,或者只是在本地创建它们。 此外,如果您的网络请求很复杂,您仍然可以创建自己的自定义结构体来封装和提供 Request
对象。
下面的示例说明了如何在 Request
上创建一个扩展,这可以大大减少在像社交网络 feed 中创建新帖子的请求时的样板代码。 它利用了 Request
中的许多默认值(所有这些都是可定制的)以保持定义的简洁
extension Request where Response == Post {
static func createPost(_ post: NewPost) -> Request<Post> {
return Request(method: .post, url: URL(string: "https://jsonplaceholder.typicode.com/posts")!, headers: [.contentType: .applicationJSON], body: try? HTTP.Body.json(post))
}
}
let createPostRequest: Request<Post> = Request(method: .post, url: URL(string: "https://jsonplaceholder.typicode.com/posts")!, headers: [.contentType: .applicationJSON], body: try? HTTP.Body.json(post))
struct CreatePostRequest {
let newPost: NewPost
var request: Request<Post> {
return Request(method: .post, url: URL(string: "https://jsonplaceholder.typicode.com/posts")!, headers: [.contentType: .applicationJSON], body: try? HTTP.Body.json(post))
}
}
对于以上示例,Post
响应类型和 NewPost
主体定义如下
struct Post: Decodable {
let id: Int
let userId: Int
let title: String
let body: String
}
struct NewPost: Encodable {
let userId: Int
let title: String
let body: String
}
为了避免必须为应用程序中的每个请求定义默认 Request
属性值,依赖 Hyperspace 提供的 RequestDefaults
会很有用。 这些甚至可以自定义
RequestDefaults.defaultCachePolicy = .reloadIgnoringLocalCacheData // Default cache policy is '.useProtocolCachePolicy'
RequestDefaults.defaultDecoder = MyCustomDecoder() // Default decoder is JSONDecoder()
我们建议通过为要与之通信的 API 的每个部分创建单独的“控制器”对象来遵守接口隔离原则。 每个控制器都应该暴露一组相关函数,并使用 BackendService
来执行请求。 但是,对于这个简单的例子,我们将直接使用 BackendService
作为视图控制器上的 private
属性
class ViewController: UIViewController {
private let backendService = BackendService()
// Rest of your view controller code...
}
假设一个视图控制器应该在用户点击“发送”按钮时创建帖子。 可能是这样的
@IBAction private func sendButtonTapped(_ sender: UIButton) {
let title = ... // Get the title from a text view in the UI...
let message = ... // Get the message from a text view/field in the UI...
let post = NewPost(userId: 1, title: title, body: message)
let createPostRequest = CreatePostRequest(newPost: post)
// Execute the network request...
}
对于上面的示例,您将如何执行请求并解析响应。 虽然所有数据转换都发生在底层 URLSession 正在使用的后台队列中,但所有 BackendService
完成回调都发生在主队列中,因此无需担心在更新 UI 之前进行线程处理。 请注意,下面成功响应的关联值的类型是 Post
结构体,如上面的 CreatePostRequest
中所定义的那样
do {
let post = NewPost(userId: 1, title: title, body: "")
let createPostRequest = Request<Post>.createPost(post)
let createdPost = try await backendService.execute(request: createPostRequest)
// Insert the new post into the UI...
} catch {
// Alert the user to the error...
}
克隆仓库
git clone https://github.com/BottleRocketStudios/iOS-Hyperspace.git
从这里,您可以打开 Hyperspace.xcworkspace
并运行示例
Models.swift
, Requests.swift
ViewController.swift
ViewController.swift
InterfaceController.swift
Hyperspace 可以通过 CocoaPods 获得。 要安装它,只需将以下行添加到您的 Podfile 中
pod 'Hyperspace'
将以下内容添加到您的 Cartfile 中
github "BottleRocketStudios/iOS-Hyperspace"
运行 carthage update
并按照 Carthage 自述文件中的步骤进行操作。
dependencies: [
.package(url: "https://github.com/BottleRocketStudios/iOS-Hyperspace.git", from: "5.0.0")
]
Hyperspace 在 Apache 2.0 许可证下可用。 有关更多信息,请参见 LICENSE.txt 文件。
请参阅 CONTRIBUTING 文档。 谢谢,贡献者!