TF-3275 Fix FCM iOS foreground desync (#3314)

This commit is contained in:
Dat Dang
2024-12-06 13:05:07 +07:00
committed by Dat H. Pham
parent 338262b4b5
commit 2fcb5f03d0
6 changed files with 74 additions and 1 deletions
+24
View File
@@ -0,0 +1,24 @@
# 55. iOS FCM routing
Date: 2024-12-05
## Status
Accepted
## Context
- Notification from FCM contains `mutable-content: true`
- Those notification will be transfered to Notification Service Extension ([See here](https://developer.apple.com/documentation/usernotifications/modifying-content-in-newly-delivered-notifications#Configure-the-payload-for-the-remote-notification)) whether the app is in background or foreground
- Due to the app is in foreground, no notification will be shown
- Due to NSE handle the data, FCM on Flutter side cannot handle it
## Decision
- We check if the app is in foreground or not
- If the app is in foreground, we will not modify the payload and route the payload to FCM foreground method channel
- If the app is in background or terminated, we will keep the current implementation
## Consequences
- Twake Mail iOS app will get latest updates when app is in foreground
+3
View File
@@ -1381,6 +1381,7 @@
F5D4EA082B2ABF090090DDFC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
APP_GROUP_ID = group.com.linagora.teammail;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
@@ -1427,6 +1428,7 @@
F5D4EA092B2ABF090090DDFC /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
APP_GROUP_ID = group.com.linagora.teammail;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
@@ -1467,6 +1469,7 @@
F5D4EA0A2B2ABF090090DDFC /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
APP_GROUP_ID = group.com.linagora.teammail;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+34 -1
View File
@@ -8,6 +8,7 @@ import flutter_local_notifications
@objc class AppDelegate: FlutterAppDelegate {
var notificationInteractionChannel: FlutterMethodChannel?
var fcmMethodChannel: FlutterMethodChannel?
var currentEmailId: String?
override func application(
@@ -17,6 +18,7 @@ import flutter_local_notifications
GeneratedPluginRegistrant.register(with: self)
createNotificationInteractionChannel()
createFcmMethodChannel()
if let payload = launchOptions?[.remoteNotification] as? [AnyHashable : Any],
let emailId = payload[JmapConstants.EMAIL_ID] as? String,
@@ -52,6 +54,14 @@ import flutter_local_notifications
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func applicationDidEnterBackground(_ application: UIApplication) {
updateApplicationStateInUserDefaults(false)
}
override func applicationWillTerminate(_ application: UIApplication) {
updateApplicationStateInUserDefaults(false)
}
override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let sharingIntent = SwiftReceiveSharingIntentPlugin.instance
if sharingIntent.hasMatchingSchemePrefix(url: url) {
@@ -69,6 +79,7 @@ import flutter_local_notifications
override func applicationDidBecomeActive(_ application: UIApplication) {
removeAppBadger()
updateApplicationStateInUserDefaults(true)
}
private func handleEmailAndress(open url: URL) -> URL? {
@@ -90,12 +101,19 @@ import flutter_local_notifications
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
TwakeLogger.shared.log(message: "AppDelegate::userNotificationCenter::willPresent::notification: \(notification)")
TwakeLogger.shared.log(message: "AppDelegate::userNotificationCenter::willPresent::notificationContent: \(notification.request.content.userInfo)")
if let notificationBadgeCount = notification.request.content.badge?.intValue, notificationBadgeCount > 0 {
let newBadgeCount = UIApplication.shared.applicationIconBadgeNumber + notificationBadgeCount
TwakeLogger.shared.log(message: "AppDelegate::userNotificationCenter::willPresent:newBadgeCount: \(newBadgeCount)")
updateAppBadger(newBadgeCount: newBadgeCount)
}
if validateDisplayPushNotification(userInfo: notification.request.content.userInfo) {
if UIApplication.shared.applicationState == .active {
fcmMethodChannel?.invokeMethod(
CoreUtils.FCM_ON_MESSAGE_METHOD_NAME,
arguments: notification.request.content.userInfo)
completionHandler([])
} else if validateDisplayPushNotification(userInfo: notification.request.content.userInfo) {
completionHandler([.alert, .badge, .sound])
} else {
completionHandler([])
@@ -167,4 +185,19 @@ extension AppDelegate {
}
}
}
private func createFcmMethodChannel() {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
self.fcmMethodChannel = FlutterMethodChannel(
name: CoreUtils.FCM_METHOD_CHANNEL_NAME,
binaryMessenger: controller.binaryMessenger
)
}
private func updateApplicationStateInUserDefaults(_ appIsActive: Bool) {
let appGroupId = (Bundle.main.object(forInfoDictionaryKey: "AppGroupId") as? String) ?? "group.\(Bundle.main.bundleIdentifier!)"
let userDefaults = UserDefaults(suiteName: appGroupId)
userDefaults?.set(appIsActive, forKey: CoreUtils.APPLICATION_STATE)
}
}
+3
View File
@@ -8,6 +8,9 @@ class CoreUtils {
static let NOTIFICATION_INTERACTION_CHANNEL_NAME = "notification_interaction_channel"
static let CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_FOREGROUND_OR_BACKGROUND = "current_email_id_in_notification_click_when_app_foreground_or_background"
static let CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_TERMINATED = "current_email_id_in_notification_click_when_app_terminated"
static let FCM_METHOD_CHANNEL_NAME = "plugins.flutter.io/firebase_messaging"
static let FCM_ON_MESSAGE_METHOD_NAME = "Messaging#onMessage"
static let APPLICATION_STATE = "applicationState"
func getCurrentDate() -> Date {
if #available(iOS 15, *) {
+2
View File
@@ -13,5 +13,7 @@
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
</dict>
<key>AppGroupId</key>
<string>${APP_GROUP_ID}</string>
</dict>
</plist>
@@ -16,6 +16,14 @@ class NotificationService: UNNotificationServiceExtension {
handler = contentHandler
modifiedContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
let appGroupId = (Bundle.main.object(forInfoDictionaryKey: "AppGroupId") as? String) ?? "group.\(Bundle.main.bundleIdentifier!)"
let userDefaults = UserDefaults(suiteName: appGroupId)
let isAppActive = userDefaults?.value(forKey: CoreUtils.APPLICATION_STATE) as? Bool
if isAppActive == true {
self.modifiedContent?.userInfo = request.content.userInfo.merging(["data": request.content.userInfo], uniquingKeysWith: {(_, new) in new})
contentHandler(self.modifiedContent ?? request.content)
}
guard let payloadData = request.content.userInfo as? [String: Any],
!keychainController.retrieveSharingSessions().isEmpty else {
self.showDefaultNotification(message: NSLocalizedString(self.newNotificationDefaultMessageKey, comment: "Localizable"))