Combine 系列
- Swift Combine 从入门到精通一
- Swift Combine 发布者订阅者操作者 从入门到精通二
- Swift Combine 管道 从入门到精通三
- Swift Combine 发布者publisher的生命周期 从入门到精通四
- Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五
- Swift Combine 订阅者Subscriber的生命周期 从入门到精通六
- Swift 使用 Combine 进行开发 从入门到精通七
- Swift 使用 Combine 管道和线程进行开发 从入门到精通八
- Swift Combine 使用 sink, assign 创建一个订阅者 从入门到精通九
- Swift Combine 使用 dataTaskPublisher 发起网络请求 从入门到精通十
- Swift Combine 用 Future 来封装异步请求 从入门到精通十一
- Swift Combine 有序的异步操作 从入门到精通十二
- Swift Combine 使用 flatMap 和 catch错误处理 从入门到精通十三
- Swift Combine 网络受限时从备用 URL 请求数据 从入门到精通十四
1. 通过用户输入更新声明式 UI
目的: 查询基于 Web 的 API 并将要显示在 UI 中的数据返回
像 Combine 这样的框架的主要好处之一是建立一个声明性结构,定义界面将如何根据用户输入进行更新。
将 Combine 与 UIKit 集成的模式是设置一个变量,该变量将保持对更新状态的引用,并使用 IBAction 连接控件。
以下示例是更大的 ViewController 实现中的代码的一部分。
这个例子与下一个模式 级联多个 UI 更新,包括网络请求 有点重叠,都建立在一个初始的发布者上。
UIKit-Combine/GithubAPI.swift
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
。
模式 级联多个 UI 更新,包括网络请求 在此代码上扩展为各种UI元素的多个级联更新。
参考
https://heckj.github.io/swiftui-notes/index_zh-CN.html
代码
https://github.com/heckj/swiftui-notes