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>
123 lines
4.3 KiB
Swift
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()
|
|
}
|
|
}
|