简体   繁体   中英

SwiftUI: Passing Data between ViewModels

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM