简体   繁体   English

SwiftUI:在 ViewModel 之间传递数据

[英]SwiftUI: Passing Data between ViewModels

Let me explain my problem/question on simple example.让我用简单的例子解释我的问题/问题。

I have first view: UserView and realted UserViewModel .我有第一个视图: UserView和 realted UserViewModel
I have second view: UserDetailsView and related UserDetailsViewModel我有第二种观点: UserDetailsView和相关的UserDetailsViewModel

In UserViewModel I'm loading User object.UserViewModel ,我正在加载User object。
And then, I want pass User object to UserDetailsViewModel然后,我想将User object 传递给UserDetailsViewModel
So that UserDetailsViewModel could manipulate with User object.这样UserDetailsViewModel就可以使用用户 object 进行操作。

Just to be clear, from clean code perspective, I don't want to keep User object in my Views.需要明确的是,从简洁代码的角度来看,我不想在我的视图中保留User object。
View should work only with ViewModel View 应该只适用于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.我将User作为@Binding传递,因为如果UserDetailsView进行了一些更新,我希望UserView进行更新。

And the problem is:问题是:
If in UserDetailsView I call updateUserName() , then UserDetailsView is not updated.如果我在UserDetailsView中调用updateUserName() ,则UserDetailsView不会更新。
Because ViewModel is a class. And only @Published can updated View .因为ViewModel是一个 class。而且只有@Published可以更新View

I know one possible solution:我知道一种可能的解决方案:
It is to add @Published var userName: String to UserDetailsViewModel .就是在UserDetailsViewModel中添加@Published var userName: String
And then update this property in updateUserName() .然后在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 ?如果User有许多我想在UserDetailsViewModel中显示的属性怎么办?
Then I should create @Published property for each?那么我应该为每个创建@Published属性吗? Or create a separate @Published var publishedUser ?或者创建一个单独的@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.在 SwiftUI 中,View 结构已经是一个视图 model(SwiftUI 区分视图结构数据以自动创建/更新/删除实际的 UIView),因此可以将 User 存储在 View 结构中的@State中,这就是它的用途。 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.您可以使用.task修饰符启动 async.network 下载,它在底层UIView出现时启动,在它消失时取消。 It would be more work to do that using an object and you might get a leak if not done correctly.使用 object 会做更多的工作,如果操作不正确,您可能会泄漏。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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