diff --git a/iosApp/iosApp/PushNotifications/PushNotificationManager.swift b/iosApp/iosApp/PushNotifications/PushNotificationManager.swift index f07bc78..1513af1 100644 --- a/iosApp/iosApp/PushNotifications/PushNotificationManager.swift +++ b/iosApp/iosApp/PushNotifications/PushNotificationManager.swift @@ -9,6 +9,14 @@ class PushNotificationManager: NSObject, ObservableObject { @Published var deviceToken: String? @Published var notificationPermissionGranted = false + private let registeredTokenKey = "com.casera.registeredDeviceToken" + + /// The last token that was successfully registered with the backend + private var lastRegisteredToken: String? { + get { UserDefaults.standard.string(forKey: registeredTokenKey) } + set { UserDefaults.standard.set(newValue, forKey: registeredTokenKey) } + } + override init() { super.init() } @@ -58,12 +66,48 @@ class PushNotificationManager: NSObject, ObservableObject { // MARK: - Backend Registration - private func registerDeviceWithBackend(token: String) async { + /// Call this after login to register any pending device token + func registerDeviceAfterLogin() { + guard let token = deviceToken else { + print("⚠️ No device token available for registration") + return + } + + Task { + await registerDeviceWithBackend(token: token, force: false) + } + } + + /// Call this when app returns from background to check and register if needed + func checkAndRegisterDeviceIfNeeded() { + guard let token = deviceToken else { + print("⚠️ No device token available for registration check") + return + } + + // Skip if token hasn't changed + if token == lastRegisteredToken { + print("📱 Device token unchanged, skipping registration") + return + } + + Task { + await registerDeviceWithBackend(token: token, force: false) + } + } + + private func registerDeviceWithBackend(token: String, force: Bool = false) async { guard TokenStorage.shared.getToken() != nil else { print("⚠️ No auth token available, will register device after login") return } + // Skip if token hasn't changed (unless forced) + if !force && token == lastRegisteredToken { + print("📱 Device token unchanged, skipping registration") + return + } + // Get unique device identifier let deviceId = await MainActor.run { UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString @@ -84,6 +128,10 @@ class PushNotificationManager: NSObject, ObservableObject { if let success = result as? ApiResultSuccess { print("✅ Device registered successfully: \(success.data)") + // Cache the token on successful registration + await MainActor.run { + self.lastRegisteredToken = token + } } else if let error = result as? ApiResultError { print("❌ Failed to register device: \(error.message)") } else { diff --git a/iosApp/iosApp/RootView.swift b/iosApp/iosApp/RootView.swift index a0a67ba..c758acc 100644 --- a/iosApp/iosApp/RootView.swift +++ b/iosApp/iosApp/RootView.swift @@ -40,6 +40,9 @@ class AuthenticationManager: ObservableObject { if let success = result as? ApiResultSuccess { self.isVerified = success.data?.verified ?? false + // Register device for push notifications for authenticated users + PushNotificationManager.shared.registerDeviceAfterLogin() + // Verify subscription entitlements with backend for verified users if self.isVerified { await StoreKitManager.shared.verifyEntitlementsOnLaunch() @@ -65,6 +68,9 @@ class AuthenticationManager: ObservableObject { func login(verified: Bool) { isAuthenticated = true isVerified = verified + + // Register device for push notifications now that user is authenticated + PushNotificationManager.shared.registerDeviceAfterLogin() } func markVerified() { diff --git a/iosApp/iosApp/iOSApp.swift b/iosApp/iosApp/iOSApp.swift index 220dc9f..08b42aa 100644 --- a/iosApp/iosApp/iOSApp.swift +++ b/iosApp/iosApp/iOSApp.swift @@ -5,6 +5,7 @@ import ComposeApp struct iOSApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @StateObject private var themeManager = ThemeManager.shared + @Environment(\.scenePhase) private var scenePhase @State private var deepLinkResetToken: String? init() { @@ -35,6 +36,12 @@ struct iOSApp: App { .onOpenURL { url in handleDeepLink(url: url) } + .onChange(of: scenePhase) { newPhase in + if newPhase == .active { + // Check and register device token when app becomes active + PushNotificationManager.shared.checkAndRegisterDeviceIfNeeded() + } + } } }