Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Having trouble initializing sub-view when navigating to it from parent?

I am trying to navigate to a subview and initialize it from a property passed from the parent view WITHOUT using onAppear.

I am able to make this work with onAppear but don’t like how it calls the function everytime the view shows up and there has to be a cleaner way??

I want to initialize PopupCollectorView by calling loadCollectors in its init() function
but I keep getting errors:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

"Variable ‘self.profileData’ used before being initialized"

"Variable ‘self.profileData’ used before being initialized"

"Return from initializer without initializing all stored properties"

How can I make the subview view call loadCollectors() with the profileData I am passing to it??

THANK YOU

PARENT VIEW – all inside a navigation view hierarchy…

struct PopupProfileFull: View {
    @ObservedObject var popupVM : PopupProfileViewModel
    
    @State var presentCollectors: Bool = false

    var body: some View {
        VStack {
            if let profileData = popupVM.profileData {
                // tapping on this triggers navigation to popupCollectorsView
                PopupAccountStats(popupVM: popupVM, presentCollectors: $presentCollectors, presentCollecting: $presentCollecting)
                .background (
                    NavigationLink("", destination: PopupCollectorsView(profileData: $popupVM.profileData), isActive: $presentCollectors)
                )
            }
        }
    }
}

CHILD VIEW NAVIGATED TO FROM PARENT

struct PopupCollectorsView: View {
    @StateObject var popupCollectorVM = PopupCollectorsViewModel()
    
    @Binding var profileData: ProfileData?
    
     // ERRORS OCCUR HERE
     init(profileData: ProfileData) {
        self.profileData = profileData
        loadCollectors()
    }
    
    var body: some View {
        VStack {
            List(0..<popupCollectorVM.collectors.count, id: \.self) { i in
                Text(popupCollectorVM.collectors[i].collectedUsername)
            }
        }
    }
    
    func loadCollectors() {
        if let profileData = profileData {
            popupCollectorVM.loadCollectors(userId: profileData.userId)
        }
    }
}

>Solution :

You can pass the profileData directly to the loadCollectors function:

struct PopupCollectorsView: View {
    @StateObject var popupCollectorVM = PopupCollectorsViewModel()
    
    @Binding var profileData: ProfileData?
    
     init(profileData: ProfileData) {
         self.profileData = profileData
         loadCollectors(data: profileData)
    }
    
    var body: some View {
        VStack {
            List(0..<popupCollectorVM.collectors.count, id: \.self) { i in
                Text(popupCollectorVM.collectors[i].collectedUsername)
            }
        }
    }
    
    func loadCollectors(data: ProfileData) {
        popupCollectorVM.loadCollectors(userId: data.userId)
    }
}

You could simplify even further by getting rid of the loadCollectors function (however, see my note at the end about using init for tasks like this):

init(profileData: ProfileData) {
  self.profileData = profileData
  popupCollectorVM.loadCollectors(userId: profileData.userId)
}

You may (hard to tell without being able to run your example) also have to use something like NavigationLazyView so that your View doesn’t reach init before the correct data is passed to it.

It’s also worth noting that generally, you want to avoid doing heavy lifting in init since SwiftUI views are transient and can be recreated often. The above solution will work if you have a high degree of confidence that you’re view will only reach init once, but in general, it’s better to rely on something like onAppear or even the init method of a @StateObject that only gets run once.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading