Let me explain my problem/question on simple example.
I have first view: UserView
and realted UserViewModel
.
I have second view: UserDetailsView
and related UserDetailsViewModel
In UserViewModel
I'm loading User
object.
And then, I want pass User
object to UserDetailsViewModel
So that UserDetailsViewModel
could manipulate with User object.
Just to be clear, from clean code perspective, I don't want to keep User
object in my Views.
View should work only with ViewModel
And here is a code:
struct UserView: View {
@ObservedObject var viewModel = UserViewModel()
var body: some View {
VStack {
Text(viewModel.user?.name ?? "")
if let userDetailsViewModel = viewModel.userDetailsViewModel {
NavigationLink {
UserDetailsView(viewModel: userDetailsViewModel)
} label: {
Text("See user Details")
}
}
}
}
}
class UserViewModel: ObservableObject {
@Published var user: User?
@Published var userDetailsViewModel: UserDetailsViewModel?
func fetchUser() {
APIClient.shared.loadUser { user in
self.user = user
self.userDetailsViewModel = = .init(user:
Binding(
get: {
self.user
},
set: {
self.user = $0
}
)
)
}
}
}
struct UserDetailsView: View {
@ObservedObject var viewModel: UserDetailsViewModel
var body: some View {
VStack {
Text(viewModel.user.name)
Text(viewModel.user.details)
}
}
}
class UserDetailsViewModel: ObservableObject {
@Binding var user: User
init(user: Binding<User>) {
_user = user
}
func updateUserName(_ name: String) {
APIClient.shared.updateUserName(name) { user in
self.user = user
}
}
}
I pass User
as @Binding
because I want UserView
to be updates if UserDetailsView
done some updates.
And the problem is:
If in UserDetailsView
I call updateUserName()
, then UserDetailsView
is not updated.
Because ViewModel
is a class. And only @Published
can updated View
.
I know one possible solution:
It is to add @Published var userName: String
to UserDetailsViewModel
.
And then update this property in updateUserName()
.
But I really feel that It's more a workaround rather then a solution.
What if User
has many properties that I want to display in UserDetailsViewModel
?
Then I should create @Published
property for each? Or create a separate @Published var publishedUser
?
Can some one tell me what is the best/correct way to achieve my goal?
Thank you
In SwiftUI the View struct is a view model already (SwiftUI diffs the view struct data to create/update/remove actual UIViews automatically) so it is fine to store the User in @State
in the View struct, that's what its for. You can use the .task
modifier to start an async.network download, it's started when the underlying UIView
appears and cancelled when it disappears. It would be more work to do that using an object and you might get a leak if not done correctly.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.