2

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?

2 Answers 2

1

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

Sign up to request clarification or add additional context in comments.

1 Comment

I'm downvoting this because you've changed my code in order to make it work without mentioning what you've done. After squinting at the gif, I can see that the key to fixing this was the addition of PlaygroundPage.current.setLiveView(v). If you fancy editing your answer to illustrate this, I'll upvote and accept it.
1

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.

1 Comment

Thanks, you're absolutely right. In transferring the code from my project to a standalone playground, I fixed the original issue and introduced a whole new one :/

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.