Files
honeyDueKMP/iosApp/iosApp/PushNotifications/AppDelegate.swift
Trey t 2965ec4031 Add actionable push notifications for iOS and Android
iOS:
- Add notification categories with action buttons (complete, view, cancel, etc.)
- Handle notification actions in AppDelegate with API calls
- Add navigation to specific task from notification tap
- Register UNNotificationCategory for each task state

Android:
- Add NotificationActionReceiver BroadcastReceiver for handling actions
- Update MyFirebaseMessagingService to show action buttons
- Add deep link handling in MainActivity for task navigation
- Register receiver in AndroidManifest.xml

Shared:
- Add navigateToTaskId parameter to App for cross-platform navigation
- Add notification observers in MainTabView/AllTasksView for refresh

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 14:23:25 -06:00

123 lines
4.3 KiB
Swift

import UIKit
import UserNotifications
import ComposeApp
class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
// Set notification delegate
UNUserNotificationCenter.current().delegate = self
// Register notification categories for actionable notifications
NotificationCategories.registerCategories()
// Request notification permission
Task { @MainActor in
await PushNotificationManager.shared.requestNotificationPermission()
}
// Clear badge when app launches
Task { @MainActor in
PushNotificationManager.shared.clearBadge()
}
// Initialize StoreKit and check for existing subscriptions
// This ensures we have the user's subscription status ready before they interact
Task {
_ = StoreKitManager.shared
print("✅ StoreKit initialized at app launch")
}
return true
}
// MARK: - App Lifecycle
func applicationDidBecomeActive(_ application: UIApplication) {
// Clear badge when app becomes active
Task { @MainActor in
PushNotificationManager.shared.clearBadge()
}
// Refresh StoreKit subscription status when app comes to foreground
// This ensures we have the latest subscription state if it changed while app was in background
Task {
await StoreKitManager.shared.refreshSubscriptionStatus()
}
}
// MARK: - Remote Notifications
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
Task { @MainActor in
PushNotificationManager.shared.didRegisterForRemoteNotifications(withDeviceToken: deviceToken)
}
}
func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
Task { @MainActor in
PushNotificationManager.shared.didFailToRegisterForRemoteNotifications(withError: error)
}
}
// MARK: - UNUserNotificationCenterDelegate
// Called when notification is received while app is in foreground
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
let userInfo = notification.request.content.userInfo
print("📬 Notification received in foreground: \(userInfo)")
Task { @MainActor in
PushNotificationManager.shared.handleNotification(userInfo: userInfo)
}
// Show notification even when app is in foreground
completionHandler([.banner, .sound, .badge])
}
// Called when user taps on notification or selects an action
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let userInfo = response.notification.request.content.userInfo
let actionIdentifier = response.actionIdentifier
print("👆 User interacted with notification - Action: \(actionIdentifier)")
print(" UserInfo: \(userInfo)")
Task { @MainActor in
// Handle action buttons or default tap
if actionIdentifier == UNNotificationDefaultActionIdentifier {
// User tapped the notification body - navigate to task
PushNotificationManager.shared.handleNotificationTap(userInfo: userInfo)
} else if actionIdentifier == UNNotificationDismissActionIdentifier {
// User dismissed the notification
print("📤 Notification dismissed")
} else {
// User selected an action button
PushNotificationManager.shared.handleNotificationAction(
actionIdentifier: actionIdentifier,
userInfo: userInfo
)
}
}
completionHandler()
}
}