Search code examples
iosswiftuipush-notificationfirebase-cloud-messaging

Running Firebase Messaging in SwiftUI


Despite getting the APN key for Push Notifications, for some reason, my app does not receive notifications.

Push Notifications and Background modes are all enabled.

The bundle ID is the same in the app and in the GoogleService-Info.plist file.

FCM gets generated, but, when I try to test, it does not work.

FirebaseAppDelegateProxyEnabled is also set to NO.

Am I doing something wrong? This is my code, please:

import SwiftUI
import SwiftData
import Firebase
import UserNotifications
import FirebaseCore
import FirebaseMessaging

@main
struct SomeApp: App {

    //Firebase
    @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
    
    //Network Monitor
      @StateObject var networkMonitor = NetworkMonitor()



    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.font, Font.custom("Avenir Next", size: 14))
                .environmentObject(networkMonitor)
        }
        
    }
}


//Firebase

class AppDelegate: NSObject, UIApplicationDelegate {
    
    let gcmMessageIDKey = "gcm.message_id"
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        
        FirebaseApp.configure()

        Messaging.messaging().delegate = self
              
        UNUserNotificationCenter.current()
          .requestAuthorization(
            options: [.alert, .sound, .badge]) { granted, _ in
            print("Permission granted: \(granted)")
            guard granted else { return }
            UNUserNotificationCenter.current().getNotificationSettings { settings in
                print("Notification settings: \(settings)")
                guard settings.authorizationStatus == .authorized else { return }
                DispatchQueue.main.async {
                  application.registerForRemoteNotifications()
                }
              }
          }

        return true
    }
    
    func application(_ application: UIApplication,
                       didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                       fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult)
                         -> Void) {
        if let messageID = userInfo[gcmMessageIDKey] {
          print("Message ID: \(messageID)")
        }
        
        guard let aps = userInfo["aps"] as? [String: AnyObject] else {
            completionHandler(.failed)
            return
          }
        print("got something, aka the \(aps)")

        // Print full message.
        print(userInfo)

        completionHandler(UIBackgroundFetchResult.newData)
      }
    
      func application(_ application: UIApplication,
                       didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Unable to register for remote notifications: \(error.localizedDescription)")
      }

      
      func application(_ application: UIApplication,
                       didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
          print("device token")
          let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
          let token = tokenParts.joined()
          print("Device Token: \(token)")
      }
    
}

extension AppDelegate: UNUserNotificationCenterDelegate {
  // Receive displayed notifications for iOS 10 devices.
  func userNotificationCenter(_ center: UNUserNotificationCenter,
                              willPresent notification: UNNotification,
                              withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions)
                                -> Void) {
    let userInfo = notification.request.content.userInfo

    // With swizzling disabled you must let Messaging know about the message, for Analytics
    // Messaging.messaging().appDidReceiveMessage(userInfo)

    // ...

    // Print full message.
    print(userInfo)

    // Change this to your preferred presentation option
      completionHandler([[.banner, .badge, .sound]])
  }

  func userNotificationCenter(_ center: UNUserNotificationCenter,
                              didReceive response: UNNotificationResponse,
                              withCompletionHandler completionHandler: @escaping () -> Void) {
    let userInfo = response.notification.request.content.userInfo

    print(userInfo)

    completionHandler()
  }
}

extension AppDelegate: MessagingDelegate {
  // [START refresh_token]
  func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
    print("Firebase registration token: \(String(describing: fcmToken))")

    let dataDict: [String: String] = ["token": fcmToken ?? ""]
    NotificationCenter.default.post(
      name: Notification.Name("FCMToken"),
      object: nil,
      userInfo: dataDict
    )
    // TODO: If necessary send token to application server.
    // Note: This callback is fired at each app startup and whenever a new token is generated.
  }

  // [END refresh_token]
}


Console Log:


Permission granted: true

Notification settings: <UNNotificationSettings: 0x3023d9290; 
authorizationStatus: Authorized, 
notificationCenterSetting: Enabled, 
soundSetting: Enabled, 
badgeSetting: Enabled, 
lockScreenSetting: Enabled, 
carPlaySetting: NotSupported, 
announcementSetting: Disabled, 
criticalAlertSetting: NotSupported, 
timeSensitiveSetting: NotSupported, 
alertSetting: Enabled, 
scheduledDeliverySetting: Disabled, 
directMessagesSetting: NotSupported, 
showsPreviewsSetting: Always, 
alertStyle: Banner, 
groupingSetting: Default providesAppNotificationSettings: No>
device token

Device Token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


Solution

  • By GOD’S Grace, this finally did it for me:

    
    import SwiftUI
    import SwiftData
    import Firebase
    import UserNotifications
    import FirebaseCore
    import FirebaseMessaging
    
    @main
    struct SomeApp: App {
    
        //Firebase
        @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
    
    
        var body: some Scene {
            WindowGroup {
                ContentView()
                    
            }
            
        }
    }
    
    
    class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
        
        let gcmMessageIDKey = "gcm.message_id"
        
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
            
            FirebaseApp.configure()
    
            // Set UNUserNotificationCenter delegate
            UNUserNotificationCenter.current().delegate = self
            
            // Request notification authorization
            UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
                print("Permission granted: \(granted)")
                guard granted else { return }
                UNUserNotificationCenter.current().getNotificationSettings { settings in
                    print("Notification settings: \(settings)")
                    guard settings.authorizationStatus == .authorized else { return }
                    DispatchQueue.main.async {
                        application.registerForRemoteNotifications()
                    }
                }
            }
            
            Messaging.messaging().delegate = self
            
            return true
        }
        
        func application(_ application: UIApplication,
                         didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
            print("Device Token: \(deviceToken.map { String(format: "%02.2hhx", $0) }.joined())")
            
            // Pass device token to FCM
            Messaging.messaging().apnsToken = deviceToken
        }
        
        func application(_ application: UIApplication,
                         didFailToRegisterForRemoteNotificationsWithError error: Error) {
            print("Failed to register for remote notifications: \(error.localizedDescription)")
        }
        
        func application(_ application: UIApplication,
                         didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                         fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
            if let messageID = userInfo[gcmMessageIDKey] {
                print("Message ID: \(messageID)")
            }
            
            print(userInfo)
            
            completionHandler(UIBackgroundFetchResult.newData)
        }
        
        // MARK: - UNUserNotificationCenterDelegate
        
        func userNotificationCenter(_ center: UNUserNotificationCenter,
                                    willPresent notification: UNNotification,
                                    withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
            let userInfo = notification.request.content.userInfo
            
            print(userInfo)
            
            // Change this to your preferred presentation option
            completionHandler([[.banner, .badge, .sound]])
        }
        
        func userNotificationCenter(_ center: UNUserNotificationCenter,
                                    didReceive response: UNNotificationResponse,
                                    withCompletionHandler completionHandler: @escaping () -> Void) {
            let userInfo = response.notification.request.content.userInfo
            
            print(userInfo)
            
            completionHandler()
        }
    }
    
    extension AppDelegate: MessagingDelegate {
        
        func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
            print("Firebase registration token: \(String(describing: fcmToken))")
            
            let dataDict: [String: String] = ["token": fcmToken ?? ""]
            NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
            
            // TODO: If necessary send token to application server.
        }
    }