macosswiftuiswiftui-navigationview

List in NavigationView is calling init for every row


I'm making a Mac app using SwiftUI.

I have a simple 2 column NavigationView. The first view is a Sidebar containing a list, the second view is a detail view.

(I know NavigationView has been deprecated, but I can't use NavigationSplitView at the moment.)

When I launch the app, the init function on the detail view is being called for every row of the list (in toy example below, init 1-10 is printed to console). Is this the expected behavior or am I doing something wrong? In my real app, the init is used to create a core data fetch request, so calling it multiple times unnecessarily is undesirable.

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView {
            SidebarView()
            EmptyView()

        }
    }
}

struct SidebarView: View {
    var body: some View {
        List{
            ForEach((1...10), id: \.self){ i in
                NavigationLink{
                    DetailView(myVar: i)
                } label: {
                    Text("myVar \(i)")
                }
            }
        }
    }
}


struct DetailView: View {
    @State var preVar:Int
    init(myVar: Int) {
        let _ = print("init \(myVar)")
        self.preVar = myVar
    }
    
    var body: some View {
        Text("Hello, World!")
    }
}

Solution

  • This is expected behaviour, but it's only the init that is called, so it's very lightweight.

    The body var is only called when the View is displayed.

    This doesn't happen when using the new iOS 16 NavigationStack as follows:

    struct ContentView: View {
        
        @State private var path = NavigationPath()
        
        var body: some View {
            NavigationStack(path: $path) {
                SidebarView(path: $path)
                EmptyView()
    
            }
        }
    }
    
    struct SidebarView: View {
        
        @Binding var path: NavigationPath
        
        var body: some View {
            List{
                ForEach((1...10), id: \.self){ i in
                    NavigationLink("myVar \(i)", value: i)
                }
            }
            .navigationDestination(for: Int.self) { i in
                DetailView(myVar: i)
            }
        }
    }