Search code examples
swiftuiswiftui-navigationlink

Switfui How set EnviromentObject from NavigationLink


I want to show, in View 2, the element I selected in View 1. I wrote this simple code, but it doesn't work, because I can't understand how to set dataStore in NavigationLink. Can anyone correct the code to make it work?

import SwiftUI

class DataStore: ObservableObject {
    @Published var itemNumber = 0
}

struct ContentView: View {

    @EnvironmentObject var dataStore:DataStore

    var body: some View {
        TabView {
            // First View Tab
            NavigationView {
                List(0..<5) { item in
                    NavigationLink(destination: DetailView().environmentObject(self.dataStore)) {
                        Text("Item: \(item)")
                    }
                }
            }
            .tabItem {
                Text("View 1")
            }            
            // Second View Tab
            Text("This is the item selected in View Tab 1: \(dataStore.itemNumber)")
                .tabItem {
                    Text("View 2")
            }
        }
    }
}

struct DetailView:View {
    @EnvironmentObject var dataStore:DataStore
    var body: some View {
        Text("Item: \(dataStore.itemNumber)")
    }
}

In SceneDelegate I set dataStore as environmentObject:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?

    var dataStore = DataStore()

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        let contentView = ContentView()

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView.environmentObject(dataStore))
            self.window = window
            window.makeKeyAndVisible()
        }
    }
}

Solution

  • If you need dependency then find where to make dependency injection.

    Here is possible approach. Tested with Xcode 11.4 / iOS 13.4

    struct ContentView: View {
    
        @EnvironmentObject var dataStore:DataStore
    
        var body: some View {
            TabView {
                // First View Tab
                NavigationView {
                    List(0..<5) { item in
                        NavigationLink(destination: DetailView(item: item)) {
                            Text("Item: \(item)")
                        }
                    }
                }
                .tabItem {
                    Text("View 1")
                }
                // Second View Tab
                Text("This is the item selected in View Tab 1: \(dataStore.itemNumber)")
                    .tabItem {
                        Text("View 2")
                }
            }
        }
    }
    
    struct DetailView:View {
        let item: Int
        @EnvironmentObject var dataStore:DataStore
        var body: some View {
            Text("Item: \(dataStore.itemNumber)")
            .onAppear {
                self.dataStore.itemNumber = self.item
            }
        }
    }