Environmentobject VS Singleton in Swiftui

EnvironmentObject vs Singleton in SwiftUI?

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.

How to create a singleton that I can update in SwiftUI

Make your singleton a ObservableObject with @Published properties:

struct ContentView: View {
@StateObject var loading = LoadingSingleton.shared

var body: some View {
if loading.isLoading {
Text("Loading...")
}
ChildView()
Button(action: { loading.isLoading.toggle() }) {
Text("Toggle loading")
}
}
}

struct ChildView : View {
@StateObject var loading = LoadingSingleton.shared

var body: some View {
if loading.isLoading {
Text("Child is loading")
}
}
}

class LoadingSingleton : ObservableObject {
static let shared = LoadingSingleton()
@Published var isLoading = false

private init() { }
}

I should mention that in SwiftUI, it's common to use .environmentObject to pass a dependency through the view hierarchy rather than using a singleton -- it might be worth looking into.

SwiftUI Classes that conforms ObservableObject should be Singleton?

Why do you think a viewmodel should be a singleton? And especially, why should an ObservableObject conformant class need a singleton instance? That's a bad idea.

Not only is this absolutely unnecessary, this would also mean you cannot have several instances of the same view on the screen without them having shared state. This is especially bad on iPad if you want to support split screen and running 2 scenes of your app on the screen at the same time.

Don't make anything a singleton, unless you absolutely have to.

The only important thing to keep in mind with storing @ObservedObjects on SwiftUI Views is that they should never be initialised inside the view. When an @ObservedObject changes (or one of its @Published properties change), the View storing it will be reloaded. This means that if you create the object inside the View, whenever the object updates, the view itself will create a new instance of said object.

So this is a bad idea and won't work:

struct ContentView: View {
// Never do this
@ObservedObject private var vm = MyViewModel()

var body: some View {
Text(vm.result)
}
}

Instead, you need to inject the viewmodel into your View (by creating it in the parent view or in a coordinator, etc, wherever you create your ContentView from).

struct ParentView: View {
@State private var childVM = MyViewModel()

var body: some View {
ContentView(vm: childVM)
}
}


struct ContentView: View {
@ObservedObject private var vm: MyViewModel

// Proper way of injecting the view model
init(vm: MyViewModel) {
self.vm = vm
}

var body: some View {
Text(vm.result)
}
}

Update UI on Singleton property change without sharing dependency between Views

The ObservableObject updates view only via ObservedObject/StateObject/EnvironmentObject (which are actually have same nature). That wrapper (one of them) actually observers changes in published properties and refreshes view.

So your case can be solved like

struct ParentView: View {
@StateObject private var networkNanager = NetworkManager.shared // << observer !!

var body: some View {
SOME_VIEW.disabled(!networkNanager.isConnected) // injects dependency !!
}
}

How does SwiftUI know what environmentObject to assign to which variable

EnvironmentObjects are identified by type.

SwiftUI just looks for environment variables for a specific type (here it's RegisterViewModel) and uses the first one it finds.

This code puts RegisterViewModel to the environment:

.environmentObject(RegisterViewModel())

Then, when building a View, SwiftUI looks for a RegisterViewModel type in the environment:

@EnvironmentObject var viewModel: RegisterViewModel

More details:

  • How to set multiple EnvironmentObjects which are same type
  • Why can't swiftui distinguish 2 different environment objects?


Related Topics



Leave a reply



Submit