I am experimenting with SwiftUI and having a hard time figuring out the proper architecture for my app.
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.
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
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. However, it seems that @EnvironmentObject is restricted to views only? If so, how do I access the global auth state from within each VM that requires it?