I'm writing my app using SwiftUI and VIPER. And to save the idea of viper(testability, protocols and etc) and SwiftUI reactivity I want to add 1 more layer - ViewModel. My presenter will ask data from interactor and will put in ViewModel, then view will just read this value.I checked does method that put data into view model works - and yes it does. But my view just don't see the property of view model (shows empty list) even if it conforms to ObservableObject and property is marked with Published. What is more interesting that if I store data in presenter and also mark it with published and observable object it will work. Thank in advance!
class BeersListPresenter: BeersListPresenterProtocol, ObservableObject{
var interactor: BeersListInteractorProtocol
@ObservedObject var viewModel = BeersListViewModel()
init(interactor: BeersListInteractorProtocol){
self.interactor = interactor
}
func loadList(at page: Int){
interactor.loadList(at: page) { beers in
DispatchQueue.main.async {
self.viewModel.beers.append(contentsOf: beers)
print(self.viewModel.beers)
}
}
}
class BeersListViewModel:ObservableObject{
@Published var beers = [Beer]()
}
struct BeersListView: View{
var presenter : BeersListPresenterProtocol
@StateObject var viewModel : BeersListViewModel
var body: some View {
NavigationView{
List{
ForEach(viewModel.beers, id: \.id){ beer in
HStack{
VStack(alignment: .leading){
Text(beer.name)
.font(.headline)
Text("Vol: \(presenter.formattedABV(beer.abv))")
.font(.subheadline)
}
Some things to note.
You can't chain
ObservableObjects so@ObservedObject var viewModel = BeersListViewModel()inside theclasswon't work.The second you have 2 ViewModels one in the
Viewand one in thePresenteryou have to pick one. One will not know what the other is doing.Below is how to get your code working
Now I am not a VIPER expert by any means but I think you are mixing concepts. Mixing MVVM and VIPER.Because in VIPER the
presenterExists below the View/ViewModel, NOT at an equal level.I found this tutorial a while ago. It is for UIKit but if we use an
ObservableObjectas a replacement for theUIViewControllerand the SwiftUIViewserves as the storyboard.It makes both the
ViewModelthat is anObservableObjectand theViewthat is a SwiftUIstructa singleViewlayer in terms of VIPER.You would get code that looks like this
If you don't want to separate the VIPER View Layer into the
ViewModeland theSwiftUIViewyou can opt to do something like the code below but it makes it harder to replace the UI and is generally not a good practice. Because you WON'T be able to call methods from thepresenterwhen there are updates from theinteractor.