I had a setup using @State
in my SwiftUI view and going all my operations in the View (loading API etc) however when attempting to restructure this away from using @ViewBuilder
and @State
and using a @ObservedObject
ViewModel, I lost the ability to dynamically change my view based on the @State
variables
My code is now
@ObservedObject private var contentViewModel: ContentViewModel
init(viewModel: ContentViewModel) {
self.contentViewModel = viewModel
}
var body: some View {
if contentViewModel.isLoading {
loadingView
}
else if contentViewModel.fetchError != nil {
errorView
}
else if contentViewModel.movies.isEmpty {
emptyListView
} else {
moviesList
}
}
However whenever these viewmodel properties change, the view doesn't update like it did when i used them in the class as @State
properties...
ViewModel is as follows:
final class ContentViewModel: ObservableObject {
var movies: [Movie] = []
var isLoading: Bool = false
var fetchError: String?
private let dataLoader: DataLoaderProtocol
init(dataLoader: DataLoaderProtocol = DataLoader()) {
self.dataLoader = dataLoader
fetch()
}
func fetch() {
isLoading = true
dataLoader.loadMovies { [weak self] result, error in
guard let self = `self` else { return }
self.isLoading = false
guard let result = result else {
return print("api error fetching")
}
guard let error = result.errorMessage, error != "" else {
return self.movies = result.items
}
return self.fetchError = error
}
}
How can i bind these 3 state deciding properties to View outcomes now they are abstracted away to a viewmodel?
Thanks
Place @Published before all 3 of your properties like so:
@Published var movies: [Movie] = []
@Published var isLoading: Bool = false
@Published var fetchError: String?
You were almost there by making the class conform to ObservableObject but by itself that does nothing. You then need to make sure the updates are sent automatically by using the @Published
as I showed above or manually send the objectWillChange.send()
Edit:
Also you should know that if you pass that data down to any children you should make the parents property be @StateObject
and the children's be ObservedObject