Issue #825
For many apps that require user authentication, a common practice is to define a shared UserManager
with an optional User
. This is convenient but it requires us to constantly unwrap and check that user
class UserManager {
static let shared = UserManager()
private(set) var user: User?
}
A more safer approach is to leverage Swift type system and separate the need based on authenticated and unauthenticated usage. For example, for an unauthorized users, we show the login screen. For authorized users but haven’t completed profile customization yet, we show onboarding. And finally, show the main screen.
Define UserState
Depend on the app, we can define a UserState
. Here, after the user has logged in with Apple ID, we need to onboard them with few questions for profile settings before taking them to the main flow.
enum UserState {
case none
case authorized(User)
case registered(User, Profile)
}
Entry AppView
Then in our entry AppView
, we can show different parts of the app depending on this userState
final class AppViewModel: ObservableObject {
@Published var userState: UserState = .none
}
struct AppView: View {
@StateObject var ViewModel = AppViewModel()
var body: some View {
content
.onAppear {
authService.checkUserState()
}
}
@ViewBuilder
private var content: some View {
switch viewModel.userState {
case .none:
LoginView()
case let .authorized(user):
OnboardView(user: user)
case let .registered(user, profile):
MainView(user: user, profile: profile)
}
}
}
Onboard flow
We only show OnboardView given that the user has authorized, this requirement needs the user
to be nonnil. Since our UserState is predictable we can just pass the user
to the Onboard flow
Note here how we use underscore _
to access the container StateObject
to initialize
final class OnboardViewModel: ObservableObject {
private let user: User
init(user: User) {
self.user = user
}
}
struct OnboardView: View {
@StateObject private var viewModel: OnboardViewModel
init(user: User) {
self._viewModel = StateObject(wrappedValue: OnboardViewModel(user: user))
}
var body: some View {}
}