[英]SwiftUI access global state from ViewModel
I am experimenting with SwiftUI and having a hard time figuring out the proper architecture for my app.我正在试验 SwiftUI 并且很难为我的应用程序找出合适的架构。
What I am trying to achieve is simple.我想要实现的目标很简单。 I want to have an initial screen that either show a sign up screen or the home screen for a given user depending on current authentication state.我想要一个初始屏幕,根据当前的身份验证状态显示给定用户的注册屏幕或主屏幕。 I cannot figure out how to make the initial screen pick up changes from the sign up screen once authentication has taken place.一旦进行身份验证,我无法弄清楚如何使初始屏幕从注册屏幕中提取更改。 Here's some relevant code:这是一些相关的代码:
struct InitialView: View {
@EnvironmentObject var viewModel: InitialViewModel
var body: some View {
VStack {
if viewModel.auth.identity != nil {
NewHomeView()
} else {
SignInView()
}
}
}
}
In the sign in view, I have a usual sign in form and when the user presses the sign in button, I want to send a login request to the backend and remember the resulting token.在登录视图中,我有一个常用的登录表单,当用户按下登录按钮时,我想向后端发送登录请求并记住生成的令牌。
class SignInViewModel: ObservableObject {
private let auth: AuthStore
private let signInApi: SignInApi
private var cancellableSet: Set<AnyCancellable>
// Input
@Published var name: String = ""
@Published var password: String = ""
@Published var showSignUp: Bool = false
// Output
@Published var authSuccess: Bool = false
init(auth: AuthStore, signInApi: SignInApi) {
self.auth = auth
self.signInApi = signInApi
self.cancellableSet = Set<AnyCancellable>()
}
func signIn() {
signInApi.signIn(email: name, password: password).sink(receiveCompletion: { _ in }) { response in
self.auth.identity = Identity(
person: Person(id: 1, name: "user", sex: nil, birthday: nil),
token: response.token
)
self.authSuccess = true
}.store(in: &cancellableSet)
}
}
HOWEVER, this does not work.但是,这不起作用。 Even after clicking the sign in button, the initial view is not updated.即使单击登录按钮后,初始视图也不会更新。 Note that I am passing the same AuthStore to both view models.请注意,我将相同的 AuthStore 传递给两个视图模型。
let auth = AuthStore()
// Create the SwiftUI view that provides the window contents.
let contentView = InitialView()
.environmentObject(InitialViewModel(auth: auth))
.environmentObject(SignInViewModel(auth: auth, signInApi: SignInApi()))
where AuthStore is defined as其中 AuthStore 定义为
class AuthStore: ObservableObject {
@Published var identity: Identity? = nil
}
Ideally, I'd love to be able to 1) have each view be paired with its own VM 2) have each VM access global state via @EnvironmentObject.理想情况下,我希望能够 1) 让每个视图与其自己的 VM 配对 2) 让每个 VM 通过@EnvironmentObject 访问全局状态。 However, it seems that @EnvironmentObject is restricted to views only?但是,似乎@EnvironmentObject 仅限于视图? If so, how do I access the global auth state from within each VM that requires it?如果是这样,我如何从需要它的每个 VM 中访问全局身份验证状态?
As on provided code I would change it as following至于提供的代码,我将其更改如下
struct InitialView: View {
@EnvironmentObject var viewModel: InitialViewModel
@EnvironmentObject var signIn: SignInViewModel
var body: some View {
VStack {
if signIn.authSuccess {
NewHomeView()
} else {
SignInView()
}
}
}
}
as well redirect publisher to main thread in以及将发布者重定向到主线程
signInApi.signIn(email: name, password: password)
.receive(on: RunLoop.main) // << following update should be on main thread
.sink(receiveCompletion: { _ in }) { response in
self.auth.identity = Identity(
person: Person(id: 1, name: "user", sex: nil, birthday: nil),
token: response.token
)
self.authSuccess = true
}.store(in: &cancellableSet)
Venture a guess here since you didn't provide InitialViewModel details.由于您没有提供 InitialViewModel 详细信息,因此请在此处进行猜测。
You didn't observe auth
in SignInViewModel
, and didn't publish it to tigger view update.您没有在SignInViewModel
观察到auth
,也没有将其发布到 tigger 视图更新。
I'm guessing it's the same problem with InitialViewModel.我猜它与 InitialViewModel 有同样的问题。
class SignInViewModel: ObservableObject {
private let auth: AuthStore
private let signInApi: SignInApi
To question 2): you can't.问题2):你不能。 (as far as I know) (据我所知)
To question 1): you can.问题1):你可以。 But at what cost?但代价是什么? Are there better alternatives?有更好的选择吗?
I can elaborate more if needed.如果需要,我可以详细说明。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.