- 目的
-
-
查询基于 Web 的 API 并将要显示在 UI 中的数据返回
-
- 参考
-
-
带有此代码的 Xcode 项目 ViewController 在 github 工程中,位于
UIKit-Combine/GithubViewController.swift
-
Publishers: @Published, URLSession.dataTaskPublisher
-
Operators: map, switchToLatest, receive, throttle, removeDuplicates
-
Subscribers: assign
-
- 另请参阅
- 代码和解释
-
像 Combine 这样的框架的主要好处之一是建立一个声明性结构,定义界面将如何根据用户输入进行更新。
将 Combine 与 UIKit 集成的模式是设置一个变量,该变量将保持对更新状态的引用,并使用 IBAction 连接控件。
以下示例是更大的 ViewController 实现中的代码的一部分。
这个例子与下一个模式 patterns.adoc 有点重叠,都建立在一个初始的发布者上。
import UIKit
import Combine
class ViewController: UIViewController {
@IBOutlet weak var github_id_entry: UITextField! (1)
var usernameSubscriber: AnyCancellable?
// username from the github_id_entry field, updated via IBAction
// @Published is creating a publisher $username of type <String, Never>
@Published var username: String = "" (2)
// github user retrieved from the API publisher. As it's updated, it
// is "wired" to update UI elements
@Published private var githubUserData: [GithubAPIUser] = []
// MARK - Actions
@IBAction func githubIdChanged(_ sender: UITextField) {
username = sender.text ?? "" (3)
print("Set username to ", username)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
usernameSubscriber = $username (4)
.throttle(for: 0.5, scheduler: myBackgroundQueue, latest: true) (5)
// ^^ scheduler myBackGroundQueue publishes resulting elements
// into that queue, resulting on this processing moving off the
// main runloop.
.removeDuplicates() (6)
.print("username pipeline: ") // debugging output for pipeline
.map { username -> AnyPublisher<[GithubAPIUser], Never> in (7)
return GithubAPI.retrieveGithubUser(username: username)
}
// ^^ type returned by retrieveGithubUser is a Publisher, so we use
// switchToLatest to resolve the publisher to its value
// to return down the chain, rather than returning a
// publisher down the pipeline.
.switchToLatest() (8)
// using a sink to get the results from the API search lets us
// get not only the user, but also any errors attempting to get it.
.receive(on: RunLoop.main)
.assign(to: \.githubUserData, on: self) (9)
-
UITextField
是从用户交互推动更新的界面元素。 -
我们定义了一个 @Published 属性,既能保存数据,又能响应更新。 因为它是一个
@Published
属性,它提供了一个发布者,我们可以使用 Combine 的管道更新界面的其他变量或元素。 -
我们从 IBAction 内部设置变量
username
,如果发布者$username
有任何订阅者,它反过来就会触发数据流更新。 -
我们又在发布者
$username
上设置了一个订阅者,以触发进一步的行为。 在这个例子中,它使用更新过的username
的值从 Github 的 REST API 取回一个 GithubAPIUser 实例。 每次更新用户名值时,它都会发起新的 HTTP 请求。 -
throttle 在这里是防止每编辑一次
UITextField
都触发一个网络请求。 throttle 操作符保证了每半秒最多可发出 1 个请求。 -
removeDuplicates 移除重复的更改用户名事件,以便不会连续两次对相同的值发起 API 请求。 如果用户结束编辑时返回的是之前的值,
removeDuplicates
可防止发起冗余请求。 -
map 在此处和 flatMap 处理错误类似,返回一个发布者的实例。 在 map 被调用时,API 对象返回一个发布者。 它不会返回请求的值,而是返回发布者本身。
-
switchToLatest 操作符接收发布者实例并解析其中的数据。
switchToLatest
将发布者解析为值,并将该值传递到管道中,在这个例子中,是一个[GithubAPIUser]
的实例。 -
在管道末尾的
assign
是订阅者,它将值分配到另一个变量:githubUserData
。
模式 patterns.adoc 在此代码上扩展为各种UI元素的多个级联更新。