我使用以下基于
UIViewController
和RxSwift/RxCocoa
的代码片段,编写一个非常简单的MVVM模式,将UIButton
的点击事件与某些Observable
工作绑定并监听结果:import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
@IBOutlet weak var someButton: UIButton!
var viewModel: ViewModel!
private var disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
viewModel = ViewModel()
setupBindings()
}
private func setupBindings() {
someButton.rx.tap
.bind(to: self.viewModel.input.trigger)
.disposed(by: disposeBag)
viewModel.output.result
.subscribe(onNext: { element in
print("element is \(element)")
}).disposed(by: disposeBag)
}
}
class ViewModel {
struct Input {
let trigger: AnyObserver<Void>
}
struct Output {
let result: Observable<String>
}
let input: Input
let output: Output
private let triggerSubject = PublishSubject<Void>()
init() {
self.input = Input(trigger: triggerSubject.asObserver())
let resultObservable = triggerSubject.flatMap { Observable.just("TEST") }
self.output = Output(result: resultObservable)
}
}
代码可以成功编译和运行,但是我需要在 SwiftUI
中将此模式变成组合模式,因此我将该代码转换为以下内容:
import SwiftUI
import Combine
struct ContentView: View {
var viewModel: ViewModel
var subscriptions = Set<AnyCancellable>()
init(viewModel: ViewModel) {
self.viewModel = viewModel
setupBindings()
}
var body: some View {
Button(action: {
// <---- how to trigger viewModel's trigger from here
}, label: {
Text("Click Me")
})
}
private func setupBindings() {
self.viewModel.output.result.sink(receiveValue: { value in
print("value is \(value)")
})
.store(in: &subscriptions) // <--- doesn't compile due to immutability of ContentView
}
}
class ViewModel {
struct Input {
let trigger: AnySubscriber<Void, Never>
}
struct Output {
let result: AnyPublisher<String, Never>
}
let input: Input
let output: Output
private let triggerSubject = PassthroughSubject<Void, Never>()
init() {
self.input = Input(trigger: AnySubscriber(triggerSubject))
let resultPublisher = triggerSubject
.flatMap { Just("TEST") }
.eraseToAnyPublisher()
self.output = Output(result: resultPublisher)
}
}
由于两个错误,此示例无法编译(在代码中已注释):
(1)问题1:如何像上面的RxSwift
案例一样,从按钮的操作闭包触发发布者的工作?
(2)问题2与架构设计有关,而不是编译错误:错误消息为:... Cannot pass immutable value as inout argument: 'self' is immutable ...
, 这是因为SwiftUI
视图是结构体,它们仅通过各种绑定(@State
、@ObservedObject
等)进行更改,我有两个与问题2相关的子问题:
[A]: 在SwiftUI
视图中“sink”一个发布者是否被认为是一种不好的做法?这可能需要一些解决方法来将存储在
[B]: 在MVVM架构模式方面,对于SwiftUI/Combine
项目来说,哪种更好:使用具有[Input[Subscribers],Output[AnyPublishers]]模式的ViewModel,还是使用具有[@Published]属性的ObservableObject
ViewModel?
ObservableObject
-它是本地、简单且自动与SwiftUI集成的。 - Asperi