Files
mobile-ios/Rosetta/RosettaApp.swift

121 lines
3.6 KiB
Swift

import SwiftUI
// MARK: - App State
private enum AppState {
case onboarding
case auth
case unlock
case main
}
// MARK: - RosettaApp
@main
struct RosettaApp: App {
init() {
UIWindow.appearance().backgroundColor = .black
// Detect fresh install: UserDefaults are wiped on uninstall, Keychain is not.
// If this is the first launch after install, clear any stale Keychain data.
if !UserDefaults.standard.bool(forKey: "hasLaunchedBefore") {
try? AccountManager.shared.deleteAccount()
UserDefaults.standard.set(true, forKey: "hasLaunchedBefore")
}
// Avoid heavy startup work on MainActor; Lottie assets load lazily on first use.
}
@AppStorage("hasCompletedOnboarding") private var hasCompletedOnboarding = false
@AppStorage("isLoggedIn") private var isLoggedIn = false
@AppStorage("hasLaunchedBefore") private var hasLaunchedBefore = false
@State private var appState: AppState?
var body: some Scene {
WindowGroup {
ZStack {
RosettaColors.Dark.background
.ignoresSafeArea()
if let appState {
rootView(for: appState)
.transition(.opacity.animation(.easeInOut(duration: 0.5)))
}
}
.preferredColorScheme(.dark)
.onAppear {
if appState == nil {
appState = initialState()
}
}
}
}
@MainActor static var _bodyCount = 0
@ViewBuilder
private func rootView(for state: AppState) -> some View {
let _ = Self._bodyCount += 1
let _ = print("⭐ RosettaApp.rootView #\(Self._bodyCount) state=\(state)")
switch state {
case .onboarding:
OnboardingView {
withAnimation(.easeInOut(duration: 0.55)) {
hasCompletedOnboarding = true
appState = .auth
}
}
case .auth:
AuthCoordinator(
onAuthComplete: {
withAnimation(.easeInOut(duration: 0.55)) {
isLoggedIn = true
appState = .main
}
},
onBackToUnlock: AccountManager.shared.hasAccount ? {
// Go back to unlock screen if an account exists
withAnimation(.easeInOut(duration: 0.55)) {
appState = .unlock
}
} : nil
)
case .unlock:
UnlockView(
onUnlocked: {
withAnimation(.easeInOut(duration: 0.55)) {
isLoggedIn = true
appState = .main
}
},
onCreateNewAccount: {
// Go to auth flow (Welcome screen with back button)
// Does NOT delete the old account Android keeps multiple accounts
withAnimation(.easeInOut(duration: 0.55)) {
appState = .auth
}
}
)
case .main:
MainTabView(onLogout: {
withAnimation(.easeInOut(duration: 0.55)) {
isLoggedIn = false
appState = .unlock
}
})
}
}
private func initialState() -> AppState {
if AccountManager.shared.hasAccount {
return .unlock
} else {
hasCompletedOnboarding = false
return .onboarding
}
}
}