Я бы предпочел использовать шаблон ObservableObject/ObservedObject
справа внизу, но возможны и другие варианты (как указано далее)
Все тестируются с Xcode 11.2 / iOS 13.2
final class ReactiveContainer<T: Equatable>: ObservableObject {
@Published var containedValue: T?
}
struct TestSwiftUIView: View {
@ObservedObject var vm: ReactiveContainer<String>
var body: some View {
Text("\(vm.containedValue ?? "<none>")")
}
init(textContainer: ReactiveContainer<String>) {
self._vm = ObservedObject(initialValue: textContainer)
}
}
Альтернативы:
Следующее исправляет ваш случай (если вы не сохраняете подписчика, издатель немедленно отменяется)
private var subscriber: AnyCancellable?
init(textContainer: ReactiveContainer<String>) {
subscriber = textContainer.$containedValue.compactMap {
print("compact map \($0)")
return $0
}.assign(to: \.viewModel, on: self)
}
Обратите внимание, что состояние представления связано только в иерархии представления, в Playground, как и у вас, он содержит только начальное значение.
Другой возможный подход, который лучше подходит для иерархии SwiftUI, -
struct TestSwiftUIView: View {
@State private var viewModel: String = "test"
var body: some View {
Text("\(viewModel)")
.onReceive(publisher) { value in
self.viewModel = value
}
}
let publisher: AnyPublisher<String, Never>
init(textContainer: ReactiveContainer<String>) {
publisher = textContainer.$containedValue.compactMap {
print("compact map \($0)")
return $0
}.eraseToAnyPublisher()
}
}