1
// NavigationState.swift
class NavigationState: ObservableObject {
    @Published var contentViewNavigation = ContentViewNavigationState()
    //@Published var selectedTab = 0
}

// App.swift
@main
struct SwiftUIDeepLinkApp: App {
    @StateObject private var navigationAppState = NavigationState()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(navigationAppState)
                .onAppear(perform: {
                    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                        print("Dispatch called")
                        navigationAppState.contentViewNavigation.selectedTab = 1
                        //navigationAppState.selectedTab = 1
                    }
                })
        }
    }
}

// ContentView.swift
class ContentViewNavigationState: ObservableObject {
    @Published var selectedTab = 0
}

struct ContentView: View {
    @EnvironmentObject var navigationState: NavigationState

    var body: some View {
        TabView(selection: $navigationState.contentViewNavigation.selectedTab) {
            HomeContainerFlow1()
                .tabItem { Text("Flow 1") }
                .tag(0)
        
            HomeContainerFlow2()
                .embededInNavigation()
                .tabItem { Text("Flow 2") }
                .tag(1)
        }.onReceive(navigationState.contentViewNavigation.$selectedTab, perform: { output in
            print("Value changed: \(output)")
        })
    }
}

As you can see, I'm defining a global state ("NavigationState") to handle the Navigation of the app (i.e Tab Selection). So, I have an ObservableObject (NavigationState) that will have properties for each screen navigation state (i.e ContentViewNavigationState - another ObservableObject).

Actual Behavior

In this example, I'm modifying the state in App.swift file inside a DispatchQueue.asyncAfter, the state is changing (because ContentView's onReceive modifier is displaying the update value) but is not triggering any update in ContentView.

Expected Behavior

The selected tab should be changed based on state.

  • The interesting part is that if I also modify another property (i.e the commented selectedTab), in that case the update is triggered, but not when only update a contentViewNavigation's property.

1 Answers1

1

A view must observe ObservableObject explicitly if want to be updated if the object has changed.

In your case you observe parent of observable but not inner observable. The contentViewNavigation in your scenario is not changed by itself, because it is just reference to inner object.

So the solution might be as in demo below

struct ContentView: View {
    @EnvironmentObject var navigationState: NavigationState

    var body: some View {
       // separate view which observe inner ObservableObject
       MainView(contentNavigation: navigationState.contentViewNavigation)
    }
}

struct MainView: View {
    @ObservedObject var contentNavigation: ContentViewNavigationState

    var body: some View {
        TabView(selection: $contentNavigation.selectedTab) {
            HomeContainerFlow1()
                .tabItem { Text("Flow 1") }
                .tag(0)
        
            HomeContainerFlow2()
                .embededInNavigation()
                .tabItem { Text("Flow 2") }
                .tag(1)
        }
    }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Thanks! But then it means that for each separated view, I will have to wrapped like in a ContentView to be able to receive the state and just pass the info required? – José Roberto Abreu Nov 16 '20 at 18:29