KMPNotifier icon indicating copy to clipboard operation
KMPNotifier copied to clipboard

Question: UNNotificationServiceExtension support on iOS?

Open tamimattafi opened this issue 1 year ago • 39 comments

Hello! Thank you for this amazing library!

I'm wondering, is there a possibility we could have support for changing push notification content for iOS? In a native app, it's usually done using UNNotificationServiceExtension, is it technically possible we could have access to such functionality directly from the library and on the Kotlin side (iosMain for instance) without having to implement such extension in every project?

tamimattafi avatar Apr 26 '24 08:04 tamimattafi

@tamimattafi How it works in ios? You can change content even app is in background, and after you get push notification you can change content?

mirzemehdi avatar Apr 26 '24 14:04 mirzemehdi

In foreground when you get notification you can modify it currently, and show it as you wanted. For this when you initialize the library for configuration parameter (showPushNotification) you need to pass false, then on listeners, you can show user any modified notication.

//Android

NotifierManager.initialize(
           configuration = NotificationPlatformConfiguration.Android(
               notificationIconResId = R.drawable.ic_launcher_foreground,
               showPushNotification = false,
           )
       )

//ios

     NotifierManager.shared.initialize(configuration: NotificationPlatformConfigurationIos(showPushNotification: false))

https://github.com/mirzemehdi/KMPNotifier?tab=readme-ov-file#platform-setup

mirzemehdi avatar Apr 26 '24 15:04 mirzemehdi

@mirzemehdi Yes, the extension service is meant to work in the background

tamimattafi avatar Apr 26 '24 15:04 tamimattafi

However when app is in background, and when receiving notification type of message, notification title and body will be shown as it is to the user as this will be handled by the system, in android it is like that at least.

But you can hack around by sending only data type of message, then based on that you can show to the user whatever you want.

mirzemehdi avatar Apr 26 '24 15:04 mirzemehdi

@mirzemehdi Is there a possibility of showing a notification from the background, using data type messages? I would be very thankful if you point out to an example. Unfortunately, I have tried something similar, and it works only if the iOS app is in the foreground, while on Android it works on both foreground and background.

tamimattafi avatar Apr 26 '24 17:04 tamimattafi

@tamimattafi Looks like using data type messages with ios when it is in background is tricky. For my case, I can get notification type of message both in background and foreground, but I also have a problem receiving data type of message when app is in background.

I also checked this one https://stackoverflow.com/questions/38166203/ios-data-notifications-with-fcm, and even setting content_available: true wasn't working for me.

If you can find another solution, you can let us know here as well!

mirzemehdi avatar Apr 28 '24 21:04 mirzemehdi

@mirzemehdi I implemented UNNotificationServiceExtension in my project, it has the following behavior and limitations:

  1. It must be in a separate target (as extension), which means it needs its own bundle ID and if you want to access data stored by the main app, you will need to use groups, also the runtime is limited to 24MB. This is very different from what we used to in android
  2. When the app is in the background, UNNotificationServiceExtension is triggered, it gives you the ability and some time to mutate the original one that came from the server, however, for some cases this might not be enough, for example, without a permission from Apple you can't ignore notifications that you don't want to display. To trigger the service, you need to set mutable-content: true in your aps payload, and you must have an alert
  3. When the app is in the foreground, UNNotificationServiceExtension is ignored, instead didReceiveRemoteNotification is called with userInfo. This is a very weird approach for an android developer, we used to have a single entry point.
  4. In android, or using localNotifier, you can set an id to your notification to be able to control it later, but that's not the case for background iOS notifications, the identifier can be set only using collapse-id, which must be inside your aps payload
  5. You might need to set UNUserNotificationCenter.delegate in your didFinishLaunchingWithOptions, because some push notifications can be ignored and they must be controlled using func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification)
  6. If your app is very large, you won't be able to use shared or composeApp in the notification extension (different target) due to memory limitations (24MB), you might need to break down your logic and create a new shared module with minimal dependencies, otherwise, the system can kill this process very unexpectedly

My experience with Push Notifications on iOS is kind of interesting but frustrating at the same time, not sure why they chose to have different entry points and different targets. Service and process systems on Android seem to me more simple and straight forward.

tamimattafi avatar Apr 29 '24 12:04 tamimattafi

@tamimattafi Thanks for detailed explanation. Look like very complicated thing :D. I need to research that for understanding better

mirzemehdi avatar Apr 29 '24 19:04 mirzemehdi

Hey Guys, I am from android background. Currently working on a KMP project and am using KMPNotifier library in it. I don't understand your above conversations about iOS. Currently I am trying to send a data type message to my app. But It is not getting received inside onPayloadData method. Is that the known issue? Do we have any workaround for that?

koushikvkk avatar Jul 02 '24 15:07 koushikvkk

@koushikvkk yeah this is currently issue, trying to work on that

mirzemehdi avatar Jul 06 '24 16:07 mirzemehdi

Ok. Thanks for the info @mirzemehdi.

koushikvkk avatar Jul 07 '24 11:07 koushikvkk

@koushikvkk for receiving data type message in ios, I wrote here https://github.com/mirzemehdi/KMPNotifier/issues/54#issuecomment-2269773896

Basically main thing that is working for me to get data type only message in ios, is to add "badge" key to aps when sending notification

mirzemehdi avatar Aug 05 '24 19:08 mirzemehdi

Hey @mirzemehdi, Thanks for the info. When I tried this, I could see data notification is getting received inside the onPayloadData callback only when the app is in foreground. When the app is in background, the data is not getting received. Do you also face the same issue?

Koushiktrimble avatar Aug 06 '24 13:08 Koushiktrimble

@Koushiktrimble yes, and no. So here is what happens when app is in background. I think this is limitation from ios side, but not sure if someone can find more information let us know please.

  1. First case: Sending only data type message (app in background): In this case when app is in background, no listener is triggered.

  2. Second case: Sending both data and notification message (app in background) In this case, no listener is triggered, but notification is shown to the user. Then when user clicks the notification, data type message is received as well, and listener is also triggered.

  3. Third case: Sending only data type message (app in foreground) No issue, Listener is triggered, and data type message is received.

  4. Fourth case: Sending both data and notification message (app in foreground) No issue, Listeners are triggered, data type message is received, and notification is shown to the user.

mirzemehdi avatar Aug 06 '24 13:08 mirzemehdi

so this is called silent notification (sending data type message only), and as I see from documentation this is not guaranteed to be received always in ios. That's why I always send both data and notification type message together.

mirzemehdi avatar Aug 06 '24 13:08 mirzemehdi

@mirzemehdi Hello! Data notifications are always received even if the app is in the background. The limitations are related to UI, you can't show anything from the background, only fetch data. So, data notifications should be something like silent or background notifications for data fetching or changing something in user cache. Never for UI.

If you want to guarantee that the user sees a notification (Display it on the UI), you should always send alerts with badge and sound properties. If you want to customize their text and style on the client side, you should implement Notification Service Extension, and add mutable-content: 1 to your aps payload.

Clicking on a notification should trigger delegate function didFinishLaunchingWithOptions, with a dictionary of data from the push notification you clicked, so you should solely rely on this when you handle clicks and never rely on the data notification.

tamimattafi avatar Aug 07 '24 02:08 tamimattafi

@tamimattafi thank you for this info. Even printing something to the logs doesn't work. is this expected then?

mirzemehdi avatar Aug 07 '24 15:08 mirzemehdi

@Koushiktrimble I encountered a problem with logging data notifications from the background as well, so I assume logs are treated as UI and when the app is on the background, they are not processed or displayed.

tamimattafi avatar Aug 07 '24 15:08 tamimattafi

@mirzemehdi @tamimattafi
May i ask about iOS, is it possible to execute code like simply NSLog a message by this method (UNNotificationServiceExtension). Even the app is fully closed by user (app is killed). So by using UNNotificationServiceExtension I can finally execute code like storing the push message data into my phone even if the app is fully closed? like closed by user?

Louisnil-AS avatar Aug 08 '24 10:08 Louisnil-AS

@Louisnil-AS You can write NSLog but it won't appear in your logs, because extensions run on a different process, you will need to switch your xcode logs process to see them. You can download things like a picture from a URL even if the app is closed, so I assume you can store things to local storage too, but data fetching and storing is recommended using data type notifications and not alerts.

Check these docs: https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension/

tamimattafi avatar Aug 08 '24 10:08 tamimattafi

@tamimattafi
Yes, because normally we can't run code when the app is killed. Like iOS doesn't allow that because of privacy issues... Did you mean this Extension can solve the problem?

Louisnil-AS avatar Aug 08 '24 10:08 Louisnil-AS

@Louisnil-AS Yes, the Extension runs and allows HTTP requests and local storage operations. But the time should not exceed few seconds, and the total memory consumption of this process should not exceed 24MB. So if you can live with these limitations, it's possible to do what you are looking for.

tamimattafi avatar Aug 08 '24 10:08 tamimattafi

@mirzemehdi hello! Are you talking about an issue where the onPayloadData() function is not called in the background? I am wondering if it is possible to have the onPayloadData() function be called when fcm is received in the background.

tmdgh1592 avatar Aug 18 '24 01:08 tmdgh1592

@tmdgh1592 onPayloadData functions is called in background if it contains both notification and data type message. But if it contains data type message (silent message), then it is not called. But check @tamimattafi 's comment too: https://github.com/mirzemehdi/KMPNotifier/issues/24#issuecomment-2272496556

mirzemehdi avatar Aug 18 '24 10:08 mirzemehdi

@mirzemehdi Thank you I tried data + notification as you said. If I include notification, it is true that push notifications are sent in the background, but the onPayloadData() function is still not called.

I tried it in postman

// url : https://fcm.googleapis.com/v1/projects/{projectId}/messages:send

{
  "message": {
    "token": "eCGO2Cbmf...",
    "data": {
      "customKey1":"...",
      "customKey2":"..."
    },
    "notification": {
        "title":"ddd",
        "body":"asdasa"
    }
  }
}

I checked your mentioned comments(#24) and tried including apns and badge instead of notification type, In the same way, only push notifications are sent, and onPayloadData() is not called. Is there perhaps a problem with the way I tried?

tmdgh1592 avatar Aug 18 '24 11:08 tmdgh1592

Correction to above request @tmdgh1592 would be adding apns -> .... badge also as below


        "message": {
            "token": device_id,
            "notification": {
                "title": "FCM Notification",
                 "body": "Notification from FCM"
            },
            "data": data,
            "apns": {
                "payload": {
                    "aps": {
                        "badge": 0,
                    },
                }
            }
        }
    

mirzemehdi avatar Aug 18 '24 11:08 mirzemehdi

@mirzemehdi Does device_id mean fcm token??

I tried this format and the result is still the same I tried logging, but I dont see anything 🥲

tmdgh1592 avatar Aug 18 '24 11:08 tmdgh1592

@tmdgh1592 yes device_id is fcm token. in logs you will not see payload data when app is in background. But when you click notification, then after app comes foreground onPayloadData will be triggered and you will get data there

mirzemehdi avatar Aug 18 '24 12:08 mirzemehdi

@mirzemehdi I thought this was the case after reading the comments you left, but onPayloadData() is not called even when I click on the notification. There is no problem at all when in the foreground, but this problem only occurs in the background.

tmdgh1592 avatar Aug 18 '24 12:08 tmdgh1592

@tmdgh1592 then make sure in ios you call NotifierManager.onApplicationDidReceiveRemoteNotification(userInfo: userInfo) on application's didReceiveRemoteNotification method.

https://github.com/mirzemehdi/KMPNotifier?tab=readme-ov-file#ios

If you did that step too and all other steps in ReadMe, not sure what can be missing

mirzemehdi avatar Aug 18 '24 12:08 mirzemehdi