I am new to swiftui growth. Making an attempt to construct my first actual app! I am nonetheless tackling navigation and authentication the place I am attempting to show the right screens whether or not the authState is unauthenticated or authenticated. nevertheless, my app is not launching on the right display screen relying on the state it simply crashes with no error message given.
I’ve a Router class with a single Routes enum for all my routes.
remaining class Router: ObservableObject {
public enum Routes: Hashable {
case touchdown
case emailAuth
case house
}
@Printed var path: [Routes] = []
func navigate(to vacation spot: Routes) {
path.append(vacation spot)
}
func navigateBack() {
path.removeLast()
}
func navigateToRoot() {
path.removeLast(path.rely)
}
}
that is my AuthViewModel the place I am utilizing firebase addStateDidChangeListener to hear for state modifications:
enum AuthState {
case unAuthenticated
case authenticated
}
@MainActor
class AuthViewModel: ObservableObject {
@AppStorage("electronic mail") var emailStore: String?
@Printed var electronic mail = ""
@Printed var errorMessage = ""
@Printed var authState: AuthState = .unAuthenticated
@Printed var person: Person?
@Printed var userEmail = ""
init() {
registerAuthStateHandler()
}
non-public var authStateHandle: AuthStateDidChangeListenerHandle?
func registerAuthStateHandler() {
if authStateHandle == nil {
authStateHandle = Auth.auth().addStateDidChangeListener{auth, person in
self.person = person
self.userEmail = person?.electronic mail ?? "(unknown)"
if (person != nil) {
self.authState = .authenticated
} else {
self.authState = .unAuthenticated
}
print("Curr authState: (self.authState)")
}
}
}
}
...
I feel I am appropriately declaring my router with @StateObject inside my view?:
struct ContentView: View {
@StateObject var authViewModel: AuthViewModel = AuthViewModel()
@StateObject var router: Router = Router()
var physique: some View {
NavigationStack(path: $router.path) {
// Textual content("Loading...")
LandingScreen(path: $router.path)
.navigationDestination(for: Router.Routes.self) { route in
swap route {
case .touchdown:
let _ = print("touchdown")
LandingScreen(path: $router.path)
case .emailAuth:
AuthEmail(path: $router.path)
case .house:
Textual content("TODO: house web page")
}
}
}
.onAppear {
print("Curr authState: (authViewModel.authState)")
if (authViewModel.authState == .authenticated) {
router.path = [.home]
} else {
router.path = [.landing]
}
print(router.path)
}
.environmentObject(router)
.environmentObject(authViewModel)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(AuthViewModel())
.environmentObject(Router())
}
}
inside my subviews, I’ve one thing like:
struct LandingScreen: View {
@EnvironmentObject var authViewModel: AuthViewModel
@Binding var path: [Router.Routes]
var physique: some View {
// ...
NavigationStack {
NavigationLink(worth: Router.Routes.emailAuth) {
Textual content("Proceed with electronic mail")
}
}
}
what I’ve tried was altering the preliminary display screen in my NavigationStack to only a easy Textual content("Loading...")
nevertheless, my display screen stays on the “loading…” display screen and doesn’t change the display screen to the LandingScreen. regardless of my print logs saying that it has set the trail appropriately:
Curr authState: unAuthenticated
[app.Router.Routes.landing]
touchdown
I’ve additionally tried transferring my onAppear code to an init() as an alternative however it’s anticipated that init is known as a number of occasions and nonetheless stays on the loading display screen in order that’s a no go…
I’ve additionally tried transferring the init() as much as the Router however I am getting the identical difficulty.