I have a MainViewModel that holds an array of enums with associated objects. However, when I use this in a SwiftUI view, the variable loses its @Published wrapper, and the UI doesn't update or get notified of changes. How can I preserve the @Published status of the variable?
Here's my code look like:
// Enum with associated types
enum ViewType: Hashable, Identifiable {
case .a(ViewModelA)
case .b(ViewModelB)
case .c(ViewModelC)
}
// classes used in associated types
class ViewModelA: ObservableObject {
@Published var listIndex: Int = 1
//some other properties
}
class ViewModelB: ObservableObject {
@Published var name: String = "hello"
//some other properties
}
class ViewModelC: ObservableObject {
@Published var isOn: Bool = false
//some other properties
}
// then I create an array in my mainViewModel
// to use in my SwiftUI view
class MainViewModel: ObservableObject {
@Published var viewTypeArray: [ViewType] = [] // add items to it based on the backend response (assume I have 4 items)
}
// then I use the viewModel in my view
struct MyView: View {
@ObservedObject viewModel: MainViewModel
init(viewModel: MainViewModel) {
self.viewModel = viewModel
}
var body: some View {
ForEach(viewModel.viewTypeArray) { view in
case .a(let viewModelA):
// I display something
// I loose the ObservableObject wrapper
// then I lose the connection to @Published wrapper properties ex: `listIndex`
// and no ui updates will trigger here
case .b(let viewModelB):
// I display something
// I loose the ObservableObject wrapper
// then I lose the connection to @Published properties ex: `name`
// and no ui updates will trigger here
case .c(let viewModelC):
// I display something
// I loose the ObservableObject wrapper
// then I lose the connection to @Published properties ex: `isOn`
// and no ui updates will trigger here
}
}
}
I tried using an ObservableObject wrapper for each child ViewModel (e.g., ViewModelA, ViewModelB) to maintain a direct connection with the view, but this didn't work. I want to use an enum with associated types, along with @ObservableObject and @Published wrappers, to ensure that changes properly update the SwiftUI view.
You can use a wrapper to selectively expose specific @Published
properties.
struct PropertyWrapper<T: ObservableObject, Content: View>: View {
@ObservedObject private var value: T
private var content: (T) -> Content
init(value: T, @ViewBuilder content: @escaping (T) -> Content) {
self.value = value
self.content = content
}
var body: some View {
content(value)
}
}
and then use it as below ex: in case .b
ForEach(viewModel.viewTypeArray) { view in
switch view {
case .a(let viewModelA):
// I display something
// I loose the ObservableObject wrapper
// then I lose the connection to @Published wrapper properties ex: `listIndex`
// and no ui updates will trigger here
case .b(let viewModelB):
PropertyWrapper(value: viewModelB) { object in
Text(object.name) // this should update the UI properly when `name` updates
}
case .c(let viewModelC):
// I display something
// I loose the ObservableObject wrapper
// then I lose the connection to @Published properties ex: `isOn`
// and no ui updates will trigger here
}
}
I hope this work as expected. have fun.