Kenan Nur
Kenan Nur

Reputation: 433

SwiftUI Classes that conforms ObservableObject should be Singleton?

I'm considered newby in SwiftUI and I have the below ViewModel. But I'm not sure MyViewModel should be singleton. Is that usage is right? And what is the best practice/usage for conforms ObservableObject?

class MyViewModel: ObservableObject {
    static let shared: MyViewModel = MyViewModel()
    
    @Published var result: String = ""
    
    private init() { }
    
    // some functions
}

struct ContentView: View {
    @ObservedObject private var vm = MyViewModel.shared
    
    var body: some View {
        Text(vm.result)
    }
}

Upvotes: 7

Views: 4984

Answers (2)

Kenan Nur
Kenan Nur

Reputation: 433

I implemented my scenario by that way. Can we say this is right way?

struct RootTabView: View {
    @State var tabSelection = 0
    @State private var listVM = ListViewModel()
    
    var body: some View {
        TabView(selection: $tabSelection) {
            ListView(vm: listVM).tabItem({
                Text("Tab 1")
            }).tag(0)
            
            //Some other tabs
        }
    }
}

struct ListView: View {
    @ObservedObject var vm: ListViewModel
    
    var body: some View {
        NavigationView {
            List(vm.toDoList, id: \.self) { toDo in
                NavigationLink(destination: DetailView(vm: vm)) {
                    Text(toDo)
                }
            }
        }
        .onAppear {
            vm.getList()
        }
    }
}

struct DetailView: View {
    @ObservedObject var vm: ListViewModel
    
    var body: some View {
        Text(vm.toDoItem)
            .onAppear {
                vm.getDetail()
            }
    }
}

class ListViewModel: ObservableObject {
    @Published var toDoList: [String] = []
    @Published var toDoItem: String = ""
    
    func getList() {
        toDoList = ["a", "b", "c"]
    }
    
    func getDetail() {
        // do some stuffs
        toDoItem = "A"
    }
}

Upvotes: 1

David Pasztor
David Pasztor

Reputation: 54735

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)
    }
}

Upvotes: 5

Related Questions