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:
"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.