56

If the entire views access the same model in an app, I think the Singleton pattern is enough. Am I right?

For example, if MainView and ChildView access the same model(e.g. AppSetting) like below, I cannot find any reason to use EnvironmentObject instead of the Singleton pattern. Is there any problem if I use like this? If it is okay, then when I should use EnvironmentObject instead of the Singleton pattern?

class AppSetting: ObservableObject {
    static let shared = AppSetting()
    private init() {}
    
    @Published var userName: String = "StackOverflow"
}
struct MainView: View {
    @ObservedObject private var appSetting = AppSetting.shared

    var body: some View {
        Text(appSetting.userName)
    }
}
struct ChildView: View {
    @ObservedObject private var appSetting = AppSetting.shared

    var body: some View {
        Text(appSetting.userName)
    }
}

Thanks in advance.

Lauren Yim
  • 12,700
  • 2
  • 32
  • 59
user2848557
  • 823
  • 1
  • 9
  • 11
  • The only problem is that you made it available for every type/function in your application. – Tony Jul 22 '20 at 16:37
  • @Tony But doesn't `@EnvironmentObject` also make it available for every type/function in your application? The only difference is you have `@EnvironmentObject var appSettings: AppSetting` spread throughout your code instead of `@ObservedObject var appSetting = AppSetting.shared`? – joshuakcockrell Feb 18 '22 at 19:37
  • Thanks for showing this pattern. I didn't know you had to make an ObservedObject in order to Access the Singleton's properties in the View Struct. I think this Pattern is much better for some instances. – Amey079 Feb 22 '23 at 17:02

3 Answers3

27

You are correct there is no reason in this case to use an EnvironmentObject. Apple even encourages to make no excessive use of EnvironmentObjects.

Nevertheless an EnvironmentObject can be great too, if you use an object in many views, because then you don't have to pass it from View A to B, from B to C and so on.

Often you find yourself in a situation where even @State and @Binding will be enough to share and update data in a view and between two views.

Kuhlemann
  • 3,066
  • 3
  • 14
  • 41
  • can you share where apple encourages to make no excessive use of EnvironmentObjects. ? i want to know why – Noe Miranda Franco Feb 10 '23 at 22:56
  • The comment you are replying to is nearly 3 years old, so thinks have become a lot better with SwiftUI and EnvironmentObject are no longer problematic in my opinion. Nevertheless you may see messages like "Update NavigationRequestObserver tried to update multiple times per frame." in your log, when many @Published variables are used in a View. I accept these as log noices and can't explain them. – Kuhlemann Feb 28 '23 at 21:49
12

I think when your app supports multiple windows via Scene (example Document based apps) maybe the singleton is not a solution and the EnvironmentObject is better. Example you want to share selectedColor. When you use a singleton, the selectedColor will be same in entire app, in every scene (in every view in window). But if you want to use separated settings the EnvironmentObject is convenient.

feca
  • 1,119
  • 16
  • 14
1

I found singleton easier because you can use it in a class vs passing it in the class via .onAppear().

Singleton:

class ViewModel {
  private let appSetting = AppSetting.shared
}

EnvironmentObject:

class SomeClass {
  private var appSetting : AppSetting?
  
  func addAppSetting(_ appSetting : AppSetting) {
    self.appSetting = appSetting
  }
}


struct MyView: View {
  @EnvironmentObject var appSetting : AppSetting
  @StateObject var someClass = SomeClass()
  var body: some View {
    ZStack {
    }
    .onAppear {
      someClass.addAppSetting(appSetting)
    }
  }  
}
justin
  • 651
  • 1
  • 8
  • 18