2

I have create the below contrived example to demonstrate my problem. In this code I am expecting the TextField to be initialised with "Test Company X" where X is the view depth based on the ObservableObject ViewModel. This is true and works fine for the first view only. Subsequent views do not get the published value on initial appearance. However as you back through the views and the onAppear triggers it does get initialised. Why is the TextField not correctly initialised on the second and subsequent views?

class TestViewModel: ObservableObject {
    @Published var companyName: String = ""
    
    func load(_ viewDepth: Int) {
        debugPrint("load: \(viewDepth)")
        
        // Mimic a network request to get data
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            debugPrint("publish: \(viewDepth)")
            self.companyName = "Test Company \(viewDepth)"
        }
    }
}

struct TestFormView: View {
    
    var viewDepth: Int
    @Binding var companyName: String
    @State private var navigateToNextView: Bool = false
    
    var body: some View {
        VStack(alignment: .leading, spacing: 3) {
            
            TextField("Company name", text: $companyName)
                .padding()
                .background(
                    RoundedRectangle(cornerRadius: 5)
                        .strokeBorder(Color.primary.opacity(0.5), lineWidth: 3)
                )
            
            Button.init("NEXT") {
                self.navigateToNextView = true
            }
            .frame(maxWidth: .infinity)
            .foregroundColor(Color.primary)
            .padding(10)
            .font(Font.system(size: 18,
                        weight: .semibold,
                        design: .default))
                .background(Color.secondary)
            .cornerRadius(Sizes.cornerRadius)
            
            NavigationLink(destination: TestView(viewDepth: viewDepth + 1), isActive: $navigateToNextView) {
                EmptyView()
            }
            .isDetailLink(false)
            
            Spacer()
        }
    }
}

struct TestView: View {
    @ObservedObject var viewModel = TestViewModel()
    var viewDepth: Int
    
    var body: some View {
        VStack {
            TestFormView(viewDepth: viewDepth, companyName: $viewModel.companyName)
        }
        .onAppear(perform: {
            self.viewModel.load(self.viewDepth)
        })
        .navigationBarTitle("View Depth \(viewDepth)")
        .padding()
    }
}

struct TestNavigationView: View {
    
    @State private var begin: Bool = false
    
    var body: some View {
        NavigationView {
            VStack {
                if begin {
                    TestView(viewDepth: 1)
                } else {
                    Button.init("BEGIN") {
                        self.begin = true
                    }
                    .frame(maxWidth: .infinity)
                    .foregroundColor(Color.primary)
                    .padding(10)
                    .font(Font.system(size: 18,
                                weight: .semibold,
                                design: .default))
                        .background(Color.secondary)
                    .cornerRadius(Sizes.cornerRadius)
                }
            }
        }
    }
}

1 Answer 1

1

Your model is recreated (due to current nature of NavigationLink)

SwiftUI 2.0

Fix is simple - use specially intended for such purpose StateObject

demo

struct TestView: View {
    @StateObject var viewModel = TestViewModel()

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

1 Comment

Thank you for taking the time to review my question. @StateObject is only available in iOS 14, is it possible to fix this in iOS 13?

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.