【发布时间】:2020-01-21 20:23:31
【问题描述】:
我使用以下基于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 来Combinify 这个模式,所以我将该代码转换为以下代码:
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)问题一:如何像上面RxSwift的情况一样,从按钮的actionclosure中触发发布者的工作?
(2) 问题 2 与架构设计有关,而不是编译错误:
错误说:... Cannot pass immutable value as inout argument: 'self' is immutable ...,那是因为SwiftUI 视图是结构,它们被设计为只能通过各种绑定(@State、@ObservedObject 等...)进行更改,我有两个相关的子问题问题2:
[A]:在SwiftUI 视图中sink 发布者是否被认为是一种不好的做法?这可能需要一些解决方法来将cancellable 存储在View 的结构范围内?
[B]:就 MVVM 架构模式而言,哪个更适合 SwiftUI/Combine 项目:使用具有 [ Input[Subscribers]、Output[AnyPublishers] ] 模式的 ViewModel,或者
ObservableObject ViewModel 带有 [@Published 属性]?
【问题讨论】:
-
如果有人开始回答他们会重复官方文档,所以从SwiftUI: State and Data Flow开始。简而言之,使用
ObservableObject- 它是原生的、简单的,并且自动与 SwiftUI 集成。
标签: ios swiftui rx-swift combine rx-cocoa