Warning, this is a specific case with Environment variables. I don't say that all @StateObjects should be Singleton (like ViewModels)! It would be wrong in my point of view.
Here is the case: In a SwiftUI iOS app, I use an ObservableObject class for settings, with @Published properties:
final class Settings: ObservableObject {
@Published var disableLockScreen = false // Just a dummy example
}
In the @main App struct, I initialize it with @StateObject and inject it in my views hierarchy:
@main
struct MyTestApp: App {
@StateObject private var settings = Settings()
var body: some Scene {
MainView()
.environmentObject(settings)
}
}
So MyTestApp owns the instance of Settings and all child Views can access it with @EnvironmentObject var settings: Settings.
OK... This is really convenient and great for Views. But...
I don't need to only access this Settings class in Views. I also need it in ViewModels that are not View structs but ObservableObject classes. In a ViewModel, I maybe want to access a property from Settings in code part (that are not directly linked to a View). For example, I have something like UIApplication.shared.isIdleTimerDisabled = settings.disableLockScreen.
To let my ViewModels access Settings, I have to inject it in their init. Like explained here or here. It is possible, of course, but to be honest, it generates a lot of code for not much and it becomes a lot more complicated to read.
So I found this accepted answer that shows an example of Singleton stored into a @StateObject. I was then thinking about changing my Settings class to be a Singleton. In MyTestApp, I only change Settings() with Settings.shared to store the Singleton instance in the App and continue to provide it to child views:
@main
struct MyTestApp: App {
@StateObject private var settings = Settings.shared
var body: some Scene {
MainView()
.environmentObject(settings)
}
}
Where it really becomes better, it is in ViewModels: They now can simply access Settings.shared instance if they need access to its property. No need of dependency injection with lot of code.
In my point of view, it is really simple, smooth and correct. But I am not sure at all. Is using a Singleton as an @StateObject that is shared in the environment a good approach? Is there something I don't see that may cause issues? Should all @StateObjects shared into environment and used by other than Views should be Singletons?