Warning, this is a specific case with Environment variables. I don't say that all @StateObject
s 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 @StateObject
s shared into environment and used by other than View
s should be Singletons?