I am creating an iOS project that will use Firebase Cloud Messaging (FCM) to deliver custom data elements, sent via Firebase/APNS notifications, from a company server to the iOS device.
The first thing I had to understand is that unlike Android, there is no similar type of 'Service' that will be able to capture and save information I'm sending, regardless if the app is in the foreground (active), background (still in memory) or not active (not in memory). Therefore, I have to use Notification messages NOT Data messages like I had designed for Android.
After much reading to understand both Apple APNS and the Firebase interface between the iOS app and APNS server, looking at countless posts on stackoverflow and other web resources, I finally figured out how to get this to work for my requirements.
When a Firebase Cloud Messaging (FCM) Notification message is sent from the server (or Firebase Console as the FCM defaults to Notification NOT Data messages), it is delivered via APNS and presented as a notification on the iOS device. When the user taps on the notification banner, iOS does the following: if the app is not running/loaded iOS launches the app, if the app is loaded/running but in the background iOS brings the app to the foreground OR if the app is in the foreground (all three cases), the message content is then delivered via func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {}.
One thing for sure, you Must Enable Background Modes and check Remote Notification, you DO NOT have to include {"content-available" : 1} in the payload.
1) Go through the APNS and Firebase setup, pretty straight forward, generate and register certificates and such.
2) In appDelegate, didFinishLaunchingWithOptions, add:
Messaging.messaging().delegate = self as? MessagingDelegate
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
}
else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
3) Then add these call back functions to appDelegate:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
// Print message ID.
if let messageID = userInfo["gcm.message_id"] {
print("\n*** application - didReceiveRemoteNotification - fetchCompletionHandler - Message ID: \(messageID)")
}
// Print full message.
print("\n*** application - didReceiveRemoteNotification - full message - fetchCompletionHandler, userInfo: \(userInfo)")
myNotificationService?.processMessage(title: userInfo["Title"] as! String
, text: userInfo["Text"] as! String, completion: { (success) in
if success {
completionHandler(.newData)
}
else {
completionHandler(.noData)
}
})
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert, .badge, .sound])
}
Very Helpful:
https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html
https://firebase.googleblog.com/2017/01/debugging-firebase-cloud-messaging-on.html