Emil Butiri
05/09/2024, 11:15 AMViewModel to a SwiftUI View ? KMM-ViewModel does it with the @StateViewModel annotation, but is there a way when only using the Google libraries?Rick Clephas
05/09/2024, 11:21 AM@State to store a regular object as a state value in your SwiftUI view.
However that only stores the object, it won’t observe state changes nor will it handle the lifecycle of the viewmodel.Emil Butiri
05/09/2024, 11:37 AMonCleared() and coroutine cancellation. Not that I’m not content with KMM-ViewModel, for which I’m very grateful, btw 😁
So I guess the multiplatform support is meant more for use in composables.Rick Clephas
05/09/2024, 11:54 AMMarcin Piekielny
05/09/2024, 12:21 PMUIHostingController available in the SwiftUI. In our KMP projects we extend it to handle the lifecycle of ViewModel. For this purpose we use the ViewModelStore which is available in multiplatform same as the ViewModel itself. When we create an instance of this controller we add given ViewModel to the store and when this instance is getting destroyed we call viewModelStore.clear() which clears the ViewModel and underlying CoroutineScope attached to it.
class UIHostingControllerWrapper<V: View, VM: ViewModel> : UIHostingController<V> {
private let viewModelStore = ViewModelStore()
init(rootView: V, viewModel: VM) {
super.init(rootView: rootView)
let key = String(describing: VM.self)
viewModelStore.put(key: key, viewModel: viewModel)
}
required init?(coder aDecoder: NSCoder) {
fatalError("Not implemented")
}
deinit {
viewModelStore.clear()
}
}
Probably you can achieve something similar in SwiftUi by scoping ViewModels to navigation destinations, not views, same as Compose does. As far as I see the navigationDestination modifier in SwiftUI has isPresented binding. It might be a good idea to clear the ViewModel when this value is set back to false. However I can't offer you a ready solution right now, as our iOS devs prefer to use UIKit navigation instead of SwiftUI so I didn't experiment with it yet 😅Emil Butiri
05/09/2024, 12:52 PMEmil Butiri
05/13/2024, 2:20 PMViewModelStore with a deinit() implementation that, like yours, simply calls clear() on the wrapped ViewModelStore. The View creates a wrapper and passes its `ViewModel`s to the store. When the View is taken off the navigation stack, it’s destroyed and the wrapper with it, calling deinit() .
class JetpackVMStoreWrapper {
private let store = ViewModelStore()
func put<VM: ViewModel>(_ vm: VM) {
store.put(key: VM.self.description(), viewModel: vm)
}
deinit {
store.clear()
}
}
struct WhateverScreen: View {
private let vmStore = JetpackVMStoreWrapper()
private let vm = WhateverViewModel()
init() {
vmStore.put(self.vm)
}
var body: some View {
EmptyView()
}
}Marcin Piekielny
05/13/2024, 2:26 PMView class which handles this vmStore.put partEmil Butiri
05/13/2024, 2:33 PMviewModel() on the android 10 sideMarcin Piekielny
05/13/2024, 2:34 PMdeinit works as expected for objects inside the View