🕌

【SwiftUI】EnvironmentObjectをNavigationLinkで遷移するViewに渡す挙動実験

2024/09/21に公開

概要

  • SwiftUIのEnvironmentObjectでは、親Viewで設定されていても、NavigationLinkで遷移する先にViewで利用できないという点に躓いた。
  • その挙動を確認する

環境

  • Xcode 15.4.0

実験コード

SampleApp.swift
class AppData: ObservableObject {
    @Published var count = 0
}

@main
struct EnvironmentObjectLearingApp: App {
    @StateObject private var appData = AppData()

    var body: some Scene {
        WindowGroup {
            TopView()
                .environmentObject(appData)
        }
    }
}
TopView.swift
class TopData: ObservableObject {
  @Published var count: Int = 0
}

struct TopView: View {
    @EnvironmentObject var appData: AppData
    @StateObject private var topData = TopData()

    var body: some View {
        NavigationStack {
            VStack {
                Text("TopView")
                
                Text("appData.count \(appData.count)")
                Button("increment") { appData.count += 1 }

                Text("topData.count \(topData.count)")
                Button("increment") { topData.count += 1 }

                Spacer().frame(height: 20)
                
                SecondView()
            }
            .environmentObject(topData)
        }
    }
}
SecondView, ThirdView, NestedView.swift
struct SecondView: View {
    @EnvironmentObject var appData: AppData
    @EnvironmentObject var topData: TopData

    var body: some View {
        VStack {
            Text("SecondView")

            Text("appData.count \(appData.count)")
            Button("increment") { appData.count += 1 }

            Text("topData.count \(topData.count)")
            Button("increment") { topData.count += 1 }

            Spacer().frame(height: 20)
            
            ThirdView()
            
            Spacer().frame(height: 20)
            
            NavigationLink {
                NestedView()
                    .environmentObject(topData)
                // 上記の.environmentObject(topData)の行が存在しない場合、NestedViewでtopDataを利用する際に以下のエラーが発生する
                // Fatal error: No ObservableObject of type TopData found. A View.environmentObject(_:) for TopData may be missing as an ancestor of this view.
            } label: {
                Text("To NestedView")
            }
        }
        .background(Color.yellow)
    }
}

struct ThirdView: View {
    @EnvironmentObject var appData: AppData
    @EnvironmentObject var topData: TopData

    var body: some View {
        VStack {
            Text("ThirdView")

            Text("appData.count \(appData.count)")
            Button("increment") { appData.count += 1 }
            
            Text("topData.count \(topData.count)")
            Button("increment") { topData.count += 1 }
        }
        .background(Color.orange)
    }
}

struct NestedView: View {
    @EnvironmentObject var appData: AppData
    @EnvironmentObject var topData: TopData

    var body: some View {
        VStack {
            Text("NestedView")
            
            Text("appData.count \(appData.count)")
            Button("increment") { appData.count += 1 }

            Text("topData.count \(topData.count)")
            Button("increment") { topData.count += 1 }
        }
        .background(Color.cyan)
    }
}

結果

  • 以下の通り、NavigationStackの中で渡されたenvironmentObject(TopData)は、NavigationLinkの先のviewに対してはあたらめて渡す必要がある
            NavigationLink {
                NestedView()
                    .environmentObject(topData)
            } label: {
                Text("To NestedView")
            }

Discussion