Add smart device token caching for push notification registration
Cache device token in UserDefaults and only register with backend when token changes. Also registers when app returns from background if token differs from cached value, reducing unnecessary API calls. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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<DeviceRegistrationResponse> {
|
||||
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 {
|
||||
|
||||
@@ -40,6 +40,9 @@ class AuthenticationManager: ObservableObject {
|
||||
if let success = result as? ApiResultSuccess<User> {
|
||||
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() {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user