Reputation: 433
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
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
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 @ObservedObject
s on SwiftUI View
s 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