Swift中的响应式编程:适用于iOS/macOS的Combine和RxSwift

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

Swift中的响应式编程:Combine和RxSwift



响应式编程是一种面向异步数据流和变化传播的编程范式。在Swift(iOS/macOS)生态系统中,实现这种范式的主要工具是Combine框架(内置于Apple SDK)和第三方库RxSwift。这两种工具都允许以声明式方式描述事件处理,减少回调函数的数量,使代码更具可读性和可预测性。



在本文中,我们将探讨响应式编程的核心概念,比较Combine和RxSwift,并提供适用于iOS和macOS的实用示例。



核心概念

响应式方法基于三个实体:



  • Publisher (Observable) — 数据源,生成事件(值、错误、完成)。
  • Subscriber (Observer) — 消费者,对事件做出响应。
  • Operator — 转换数据流的函数(过滤、变换、组合)。


Combine中一个简单的Publisher示例:

import Combine

let publisher = Just("Hello, Reactive World!")let subscription = publisher.sink { value in print(value)}// 输出:Hello, Reactive World!


Combine vs RxSwift:比较

两个框架解决类似的问题,但存在差异:



  • Combine — Apple原生框架(iOS 13+,macOS 10.15+)。使用强类型,并与SwiftUI和URLSession集成。
  • RxSwift — 跨平台库,拥有更大的社区。支持更旧的iOS版本(从iOS 8开始)。
  • 性能:Combine针对Apple Silicon进行了优化,RxSwift在自定义场景中可能更灵活。


iOS/macOS代码示例



1. 响应式文本字段处理(Combine + UIKit)

订阅UITextField中的文本变化:

import UIKitimport Combine

class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! private var cancellables = Set<AnyCancellable>() override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: textField) .compactMap { ($0.object as? UITextField)?.text } .debounce(for: .milliseconds(500), scheduler: RunLoop.main) .sink { text in print("用户输入了:\\(text)") // 此处可进行网络请求或更新UI } .store(in: &cancellables) }}


2. 组合多个流(macOS的RxSwift)

合并两个observable序列的示例:

import RxSwift

let disposeBag = DisposeBag()

let firstObservable = Observable.of(1, 2, 3)let secondObservable = Observable.of("A", "B", "C")

Observable.zip(firstObservable, secondObservable) .subscribe(onNext: { (number, letter) in print("\\(number) -> \\(letter)") }) .disposed(by: disposeBag)

// 输出:// 1 -> A// 2 -> B// 3 -> C


3. 使用Combine处理网络请求

使用URLSession加载数据:

import Combineimport Foundation

struct Post: Decodable { let id: Int let title: String}

func fetchPosts() -> AnyPublisher<[Post], Error> { let url = URL(string: "https://jsonplaceholder.typicode.com/posts")! return URLSession.shared.dataTaskPublisher(for: url) .map(\\.data) .decode(type: [Post].self, decoder: JSONDecoder()) .eraseToAnyPublisher()}

推荐