I'm seeking clarity on the reasonings - if any known - why the order of initialisation and view modifiers occurs.
I couldn't find any documentation on this but hoping someone might know why.
I have the following example code:
@main
struct TestApp: App {
init() { print("@main: init" }
var body: some Scene {
WindowGroup {
ContentView()
.onAppear { let _ = print("@main: onAppear" }
}
}
}
struct ContentView: View {
var body: some View {
VStack {}
.onAppear { let _ = print("ContentView: onAppear" }
}
}
When this runs, the console outputs the following:
@main - init
ContentView - onAppear
@main - onAppear
Does anyone know why or any reasoning why the order would be init, child view .onAppear, then main .onAppear?
I guess in my mind the order would be initialising all of the @main including the modifiers before any of the child views - but maybe there is a proper reason for the current order.
The documentation for
onAppearsays:So depending on the type of view,
onAppearcould totally be called in any order, as long as it is "before the first rendered frame appears".Here is a possible explanation for your particular example.
If you just "flatten"
ContentView, you end up with aVStackwith 2onAppears:Each
onAppearis modifying everything "above" it. "@main: onAppear" is printed when everything above// 2has "appeared". That means that theVStackmust have already appeared. Therefore, "@main: onAppear" is printed after "ContentView: onAppear".And of course, "@main: init" is the first thing being printed, because without creating an instance of
TestApp, SwiftUI cannot call the getter ofbody, and hence cannot know anything about what views you have.From my experiments, most SwiftUI "containers" have their
onAppearcalled first, before theonAppearof the views in them.This aligns with your expectation, if I understand correctly. The
VStackcan appear first, without any subviews, then the subviews appear. YourContentViewcannot do something similar - it is literally just a wrapper around a singleVStack.