From my experience, I did the shared view model following what I would have done with Compose or Android Views and had the view model expose a single state property that represented the current state of the view. This worked fine, until I tried to use it again in SwiftUI. SwiftUI's desire to use two way bindings for everything, made we have to build a second view model that wrapped the original view model and creating the binding that SwiftUI expected. They were dumb view models, but still annoying to write.
This did make we wish I had kept the original view model dumber, made it platform specific, and but the shared logic into use cases. I do think there are libraries out there though, that have solved this problem already.