Reputation: 7687
I have a TextField
which is user editable, but may also be updated by changes to a ViewModel
.
There seems to be some arcane magic determining when my TextField
updates itsself, however.
Here's a playground:
import Combine
import SwiftUI
class ViewModel: ObservableObject {
@Published var text: String = "0"
private var cancellables: [AnyCancellable] = []
init() {
// output what the value of `text` is, whenever it changes
let c = $text.print().sink { _ in }
cancellables.append(c)
}
func fetch() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.text += "3"
}
}
deinit { print("deinit") }
}
struct V: View {
@ObservedObject var viewModel = ViewModel()
init() {
viewModel.text += "1"
}
var body: some View {
TextField("TextField", text: $viewModel.text)
.onAppear { self.viewModel.text += "2" }
.onAppear { self.viewModel.fetch() }
}
}
var v: V? = V()
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
v = nil
}
I can see from the print()
in ViewModel.init()
that ViewModel.text
is updated as expected. Final value: 0123
.
What I don't understand: why does the TextField
value stop updating when it reaches 012
?
Upvotes: 2
Views: 200
Reputation: 17534
It works even in Playground.
To see the resulting View in Playground, you need to tell the Playground to show it.
import PlaygroundSupport
and
PlaygroundPage.current.setLiveView(v)
will do the trick. Finally I also change
var body: some View {
VStack {
TextField("TextField", text: $viewModel.text).font(.largeTitle)
.onAppear {
self.viewModel.text += "2"
self.viewModel.fetch()
}
Text(viewModel.text).font(.largeTitle).frame(width: 200)
}
}
so all changes will be directly visible.
So or so, testing in real environment is necessary.
Upvotes: 1
Reputation: 257493
It is Playground-specific behaviour, because it, seems, recreate view on every update (even from view model), so fetch
just not received. Put this code in project and test in Live Preview or Simulator - all works well.
Tested with Xcode 11.2 / iOS 13.2.
Upvotes: 1