1

I have a NavigationView which navigates to a first detail view. From this first detail view it navigates to a second detail view. In this second detail view I compose the UI observing a detailViewModel enum that stores the state: notRequested, loading and loaded. It works fine until I add the @Environment(.dismiss) var dismiss in the first detail view, when this occurs the view model enum changes but the UI is not repainted. Here is the sample code:

import SwiftUI

struct TestParentView: View {
    var body: some View {
        NavigationView {
            Button {
                
            } label: {
                NavigationLink {
                    TestDetailView()
                } label: {
                    Text("Go to detail")
                    }
            }
        }
    }
}

struct TestDetailView: View {
    // THIS LINE PROVOKES TestDetail2View NOT UPDATING VIEW
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        Button {
            
        } label: {
            NavigationLink {
                TestDetail2View()
            } label: {
                Text("Go to detail 2")
            }
        }
    }
}

struct TestDetail2View: View {
    @ObservedObject var testDetail2VM = TestDetail2VM()
    
    var body: some View {
        content
    }
    
    @ViewBuilder
    private var content: some View {
        switch testDetail2VM.state {
        case .notRequested:
            Text("")
                .onAppear {
                    Task {
                        await testDetail2VM.getData()
                    }
            }
        case .loading:
            Text("Loading data...")
        case .loaded:
            Text("LOADED VIEW!")
        }
    }
}

enum TestDetailState {
    case notRequested
    case loading
    case loaded
}

final class TestDetail2VM: ObservableObject {
    @Published var state = TestDetailState.notRequested
    
    @MainActor func getData() async {
        state = .loading
        
        try! await Task.sleep(nanoseconds: 2_000_000_000)
        
        state = .loaded
    }
}

I've observed several things:

  1. The problem only occurs if the view is the second (or more) view navigated. This problem doesn't occur if there are two vies in the navigation stack

  2. If the view model has the annotation @StateObject instead of @ObservedObject, the problem doesn't occur (not a solution due to maybe an observed object is needed in that view)

rai212
  • 711
  • 1
  • 6
  • 20
  • 2
    You need `StateObject` there, it preserves views state, `ObservedObject` is for the case when view model is injected from outside and has own life-cycle. Injected Environment probably updates parent that result in recreating TestDetail2View. – Asperi Aug 06 '22 at 11:39
  • @Asperi thanks for the answer. The real problem was instantiating the ObservedObject in the proper view. As you say, using StateObject solves the problem but if I need an ObservedObject the solution would be instantiate it in the first view and inject it in the detail views – rai212 Aug 06 '22 at 17:43

0 Answers0