I want to load some data when my app is first launched, and when the app is foregrounded I want to ensure I have the latest data.
The state is stored in a ViewModel class, which my view owns as a @StateObject. I read the ScenePhase from the Environment, and in onChange(of: scenePhase), I call a method on my ViewModel to start the reload if needed.
But when should I start the initial load?
ContentView.initis too early, becausescenePhaseis.background. And even if it were.active, I'm apparently not supposed to access StateObject from init — SwiftUI logs a runtime warning.ViewModel.initis too early as well — theoretically, I think the view model could be created even if the app were never brought to the foreground.- The first time
var bodyis accessed, the scenePhase is.active.onChange(of:)doesn't call the closure for its initial value, so it's never called until I background and re-foreground the app.
class ViewModel: ObservableObject {
init() {
// 1. reload() here? Could happen without the app entering the foreground.
}
func reload() { ... }
}
struct ContentView: View {
@Environment(\.scenePhase) var scenePhase
@StateObject var viewModel = ViewModel()
init() {
// 2. viewModel.reload() here?
// - problem 1: scenePhase == .background, not .active
// - problem 2: not supposed to access a @StateObject here anyway
}
var body: some View {
(...)
// The initial render happens when scenePhase == .active,
// so I don't get the onChange callback until it changes again.
.onChange(of: scenePhase) {
if $0 == .active {
viewModel.reload()
}
}
}
}