feat(ios): show notifications in foreground and add visual feedback

Implement UNUserNotificationCenterDelegate in AppDelegate to display
notifications when app is in foreground. Add visual feedback indicator
in test app UI to confirm notification delivery.

Changes:
- AppDelegate: Conform to UNUserNotificationCenterDelegate protocol
- AppDelegate: Implement willPresent and didReceive delegate methods
- AppDelegate: Set delegate at multiple lifecycle points to ensure
  it's always active (immediate, after Capacitor init, on app active)
- UI: Add notification received indicator in status card
- UI: Add periodic check for notification delivery (every 5 seconds)
- UI: Add instructions on where to look for notification banner
- Docs: Add IOS_LOGGING_GUIDE.md for debugging iOS logs

This fixes the issue where scheduled notifications were not visible
when the app was in the foreground. The delegate method now properly
presents notifications with banner, sound, and badge options.

Verified working: Logs show delegate method called successfully when
notification fires, with proper presentation options set.
This commit is contained in:
Matthew
2025-11-19 01:15:20 -08:00
parent ee0e85d76a
commit 3d9254e26d
3 changed files with 383 additions and 6 deletions

View File

@@ -3,9 +3,10 @@ import Capacitor
import BackgroundTasks
import DailyNotificationPlugin
import ObjectiveC
import UserNotifications
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
@@ -103,9 +104,64 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// NOTE: Background task registration is handled by DailyNotificationPlugin.load()
// Do NOT register here to avoid duplicate registration crash
// Set notification center delegate to show notifications in foreground
// Set immediately and also after Capacitor initializes to ensure it's always set
UNUserNotificationCenter.current().delegate = self
NSLog("DNP-DEBUG: UNUserNotificationCenter delegate set to AppDelegate (immediate)")
// Also set after Capacitor initializes (in case it resets the delegate)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
UNUserNotificationCenter.current().delegate = self
NSLog("DNP-DEBUG: UNUserNotificationCenter delegate re-set after Capacitor init")
}
// Override point for customization after application launch.
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
// Re-set delegate when app becomes active (in case Capacitor resets it)
UNUserNotificationCenter.current().delegate = self
NSLog("DNP-DEBUG: UNUserNotificationCenter delegate re-set in applicationDidBecomeActive")
}
// MARK: - UNUserNotificationCenterDelegate
/**
* Show notifications even when app is in foreground
* This is required for test app to see notifications during testing
*/
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
NSLog("DNP-DEBUG: ✅ userNotificationCenter willPresent called!")
NSLog("DNP-DEBUG: Notification received in foreground: %@", notification.request.identifier)
NSLog("DNP-DEBUG: Notification title: %@", notification.request.content.title)
NSLog("DNP-DEBUG: Notification body: %@", notification.request.content.body)
NSLog("DNP-DEBUG: Current delegate: %@", UNUserNotificationCenter.current().delegate != nil ? "SET" : "NOT SET")
// Show notification with banner, sound, and badge
// Use .banner for iOS 14+, fallback to .alert for iOS 13
if #available(iOS 14.0, *) {
completionHandler([.banner, .sound, .badge])
} else {
completionHandler([.alert, .sound, .badge])
}
NSLog("DNP-DEBUG: ✅ Completion handler called with presentation options")
}
/**
* Handle notification tap/interaction
*/
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
NSLog("DNP-DEBUG: Notification tapped: %@", response.notification.request.identifier)
NSLog("DNP-DEBUG: Action identifier: %@", response.actionIdentifier)
// Handle notification tap if needed
// For test app, we just log it
completionHandler()
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
@@ -121,10 +177,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}