在 RxSwift 中,很容易将 aDriver
或 a Observable
in a绑定到 a 中的View Model
某个观察者ViewController
(即 a UILabel
)。
我通常更喜欢构建一个管道,使用从其他 observables 创建的 observables,而不是“强制性地”推送值,比如通过 a PublishSubject
)。
让我们使用这个例子:从网络获取一些数据后更新 aUILabel
RxSwift + RxCocoa 示例
final class RxViewModel {
private var dataObservable: Observable<Data>
let stringDriver: Driver<String>
init() {
let request = URLRequest(url: URL(string:"https://www.google.com")!)
self.dataObservable = URLSession.shared
.rx.data(request: request).asObservable()
self.stringDriver = dataObservable
.asDriver(onErrorJustReturn: Data())
.map { _ in return "Network data received!" }
}
}
final class RxViewController: UIViewController {
private let disposeBag = DisposeBag()
let rxViewModel = RxViewModel()
@IBOutlet weak var rxLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
rxViewModel.stringDriver.drive(rxLabel.rx.text).disposed(by: disposeBag)
}
}
组合 + UIKit 示例
在基于 UIKit 的项目中,您似乎可以保持相同的模式:
- 视图模型公开发布者
- 视图控制器将其 UI 元素绑定到这些发布者
final class CombineViewModel: ObservableObject {
private var dataPublisher: AnyPublisher<URLSession.DataTaskPublisher.Output, URLSession.DataTaskPublisher.Failure>
var stringPublisher: AnyPublisher<String, Never>
init() {
self.dataPublisher = URLSession.shared
.dataTaskPublisher(for: URL(string: "https://www.google.it")!)
.eraseToAnyPublisher()
self.stringPublisher = dataPublisher
.map { (_, _) in return "Network data received!" }
.replaceError(with: "Oh no, error!")
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}
final class CombineViewController: UIViewController {
private var cancellableBag = Set<AnyCancellable>()
let combineViewModel = CombineViewModel()
@IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
combineViewModel.stringPublisher
.flatMap { Just($0) }
.assign(to: \.text, on: self.label)
.store(in: &cancellableBag)
}
}
SwiftUI 呢?
SwiftUI 依赖于属性包装器@Published
和协议ObservableObject
,ObservedObject
来自动处理绑定(从Xcode 11b7 开始)。
由于(AFAIK)属性包装器不能“动态创建”,因此您无法使用相同的模式重新创建上面的示例。以下不编译
final class WrongViewModel: ObservableObject {
private var dataPublisher: AnyPublisher<URLSession.DataTaskPublisher.Output, URLSession.DataTaskPublisher.Failure>
@Published var stringValue: String
init() {
self.dataPublisher = URLSession.shared
.dataTaskPublisher(for: URL(string: "https://www.google.it")!)
.eraseToAnyPublisher()
self.stringValue = dataPublisher.map { ... }. ??? <--- WRONG!
}
}
我能想到的最接近的方法是订阅您的视图模型(UGH!)并强制更新您的 property,这根本感觉不正确和被动。
final class SwiftUIViewModel: ObservableObject {
private var cancellableBag = Set<AnyCancellable>()
private var dataPublisher: AnyPublisher<URLSession.DataTaskPublisher.Output, URLSession.DataTaskPublisher.Failure>
@Published var stringValue: String = ""
init() {
self.dataPublisher = URLSession.shared
.dataTaskPublisher(for: URL(string: "https://www.google.it")!)
.eraseToAnyPublisher()
dataPublisher
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: {_ in }) { (_, _) in
self.stringValue = "Network data received!"
}.store(in: &cancellableBag)
}
}
struct ContentView: View {
@ObservedObject var viewModel = SwiftUIViewModel()
var body: some View {
Text(viewModel.stringValue)
}
}
在这个没有 UIViewController 的新世界中,“旧的绑定方式”会被遗忘和取代吗?