Tuesday, 7 September 2021

FCM data messages not being received by iOS Simulator or physical device

A project I'm working on requires the use of FCM Push Notifications & Messages for VoIP control amongst others.

Problem:

My iOS physical device receives push notifications, but only when the app is in the background or closed but NOT when in the foreground. Further, it does NOT receive any fcm message (data) notifications at all such as messages with only data and no notification title/body i.e. VoIP status message.


My setup steps are as follows (a detailed setup and general flow I followed can be found here):

  1. Register a (Info.plist) (see here)

  2. Download & install GoogleService-Info.plist with Xcode (see here)

  3. Create APNS key with enabled Push Notifications and uploaded .p8 file to Firebase (see here)

  4. Modify with Swift code:

AppDelegate.swift code:

import UIKit
import Flutter
import Firebase // this is added, and (see further down)

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    FirebaseApp.configure() //add this before the code below
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}
  1. Add capabilities to iOS

Info.plist file capabilities (background fetching, notifications, etc)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>BGTaskSchedulerPermittedIdentifiers</key>
    <array>
        <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    </array>
    ...
    <key>NSCameraUsageDescription</key>
    <string>Allows taking a self-portrait profile image or sending a photo while chating</string>
    <key>NSLocationAlwaysUsageDescription</key>
    <string>Allows using your location to make determining country &amp; location picking easier (optional)</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>Allows using micrphone to chat with someone when making a call</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>Allows opening files from gallery</string>
    <key>UIBackgroundModes</key>
    <array>
        <string>audio</string>
        <string>bluetooth-central</string>
        <string>external-accessory</string>
        <string>fetch</string>
        <string>location</string>
        <string>processing</string>
        <string>remote-notification</string>
        <string>voip</string>
    </array>
    <key>UILaunchStoryboardName</key>
    <string>LaunchScreen</string>
    <key>UIMainStoryboardFile</key>
    <string>Main</string>
    <key>FirebaseAppDelegateProxyEnabled</key>
    <false/>
    <key>UISupportedInterfaceOrientations</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
    <key>UISupportedInterfaceOrientations~ipad</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationPortraitUpsideDown</string>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
    <key>UIViewControllerBasedStatusBarAppearance</key>
    <false/>
</dict>
</plist>

Note: background modes as shown here cannot be enabled, rather I enabled them using a set of checkboxes to enable Background Fetch, etc.

  1. FCM - Listen and display and notifications/messages using some notification (display) service such as awesome notifications

Implementation follows these guidelines:

Foreground messages:

FirebaseMessaging.onMessage.listen((RemoteMessage message) {
  print('Got a message whilst in the foreground!');
  ...
});

Background messages:

Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();

  print("Handling a background message: ${message.messageId}");
}

void main() {
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  runApp(MyApp());
}

Problem extension:

While debugging, if the iOS app is in foreground/background and a test notification or a valid VoIP call (FCM data message) is sent to the device, the app never receives or (nor does the required breakpoint hit either).

What can cause the app/iOS not to receive these FCM messages?


Sample VoIP message sent from Firebase cloud functions service:

// build notification on liked status
const payload = {
    token: fcmToken,
    data: {
        uid: context.auth.uid,
        room_id: roomId,
        ...
    },
    android: {
        priority: 'high',
        ttl: 0,
        notification: {
            priority: 'max',
            defaultSound: true,
            defaultVibrateTimings: true,
            sticky: true,
            clickAction: "FLUTTER_NOTIFICATION_CLICK",
        }
    },
    notification: {
        title: `Incoming call from ${name}`,
        body: `Accept/Reject call`,
        // icon: 'your-icon-url',                   // Add profile image URL here if necessary
        // clickAction: 'FLUTTER_NOTIFICATION_CLICK' // required only for onResume or onLaunch callbacks
    },
    apns: {
        payload: {
            aps: {
                category: "NEW_MESSAGE_CATEGORY",
                contentAvailable: true,
            }
        }
    },
    headers: {
        "apns-push-type": "background",
        "apns-priority": "5", // Must be `5` when `contentAvailable` is set to true.
        "apns-topic": "io.flutter.plugins.firebase.messaging", // bundle identifier
    },
};

await admin.messaging().send(payload);


from FCM data messages not being received by iOS Simulator or physical device

No comments:

Post a Comment