sam-w
sam-w

Reputation: 7687

Why does binding not work between View and ViewModel in Playgrounds?

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

Answers (2)

user3441734
user3441734

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.

enter image description here

Upvotes: 1

Asperi
Asperi

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

Related Questions