ObservableObject as a singleton vs EnvironmentObject

Any opinions on creating ObservableObjects as a singleton vs injecting a objects as EnvironmentObjects? Example 1, the singleton, works, but I'm not a fan


// MARK: - Example 1

class Singleton: ObservableObject {
    @Published var someProperty: Bool

    static var shared = Singleton()

    private init() {
        someProperty = true
    }
}

struct SomeView: View {
    @ObservedObject var singleton = Singleton.shared

    var body: some View {
        Text("Hello Woild")
    }
}

// MARK: - Example 2

class SomeClass: ObservableObject {
    @Published var someProperty: Bool

    init() {
        someProperty = true
    }
}

struct SomeApp: App {
    @StateObject var someClass = SomeClass()

    var body: some Scene {
        WindowGroup {
            SomeView()
                .environmentObject(someClass)
        }
    }
}


struct SomeOtherView: View {
    @EnvironmentObject var someClass: SomeClass

    var body: some View {
        Text("Hello Woild")
    }
}```

Singletons are bad as they do not adhere to any memory deallocation rules when not manually implemented. There is a reason for injecting the object into the environment as it is made available to the entire app without the need creating singletons. https://dev.to/amrtaher1234/why-singletons-are-bad-220o

Environment objects are better as you can just attach it in the @main and access everywhere in the app. But both of them have it's own use cases.

There is nothing wrong with the singleton pattern except you have to be careful with concurrency: you should probably mark your singleton class as @MainActor. This pattern is useful if you want to trigger something in the views in the code that doesn't have access to the view hierarchy. If it's not the case then this pattern is not required, just use plain environment values.

I use this pattern for the global isLoggedIn flag to trigger re-rendering of the root view when it changes.

So this is also possible:

@MainActor
class Singleton: ObservableObject {
    @Published var flag: Bool = true

    static var shared = Singleton()
}

struct SomeView: View {
    @StateObject var singleton = Singleton.shared

    var body: some View {
        Text("Hello, Singleton\(singleton.flag ? "!" : "?")")
            .environmentObject(singleton)
    }
}

struct SomeOtherView: View {
    @EnvironmentObject var singleton: Singleton

    var body: some View {
        Text("Hello again, Singleton\(singleton.flag ? "!" : "?")")
    }
}

// Somewhere else in the code outside of Views:

Singleton.shared.flag = false

ObservableObject as a singleton vs EnvironmentObject
 
 
Q