24

I tried to implement the new Notification Service Extension, but I have a problem.

In my NotificationService.swift file I have this code:

class NotificationService: UNNotificationServiceExtension {

var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?

override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    self.contentHandler = contentHandler
    bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

    if let bestAttemptContent = bestAttemptContent {
        // Modify the notification content here...
        bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"

        print(bestAttemptContent.body)

        contentHandler(bestAttemptContent)
    }
}

override func serviceExtensionTimeWillExpire() {
    // Called just before the extension will be terminated by the system.
    // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
    if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
        contentHandler(bestAttemptContent)
    }
}
}

When I got a push notification the didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) method never called.

Maybe I misunderstood how this extension is working?

mfaani
  • 33,269
  • 19
  • 164
  • 293
just
  • 1,900
  • 4
  • 25
  • 46

10 Answers10

50

Check your deployment target on Service Extension.

I had deployment target set to 10.2 when testing on device with 10.1 and extension wasn't called until I changed it.

Another issue might be debugging.. In order to make it work You need to attach Service Extension Process. In Xcode menu Debug > Attach To Process > Name of your extension

Saurabh Prajapati
  • 2,348
  • 1
  • 24
  • 42
Kamilton
  • 696
  • 6
  • 12
  • 5
    For further clarification around this: Looks like Apple defaults the Deployment Target of the new extension to the latest iOS version even if your base app targets an older version. – Justin Vallely Apr 10 '18 at 17:49
  • Probably this is the correct answer for most cases. Only setting deployment target properly worked for me, no matter which scheme I'm running. And don't forget to push the payload in correct form. +1 – choofie Jul 17 '18 at 13:11
  • Excellent; saved me bags of time!! – user3069232 Sep 27 '18 at 13:35
  • You are my hero. – Luke Dec 05 '18 at 11:13
  • Just wasted 2 hours of my life to debug a Flutter app with a new Notification Service Extension for Firebase. I can't even change the notification title! Turned out the appex is not being run because my device is not on the latest iOS version... Thank you very much Kamilton. – mrpaint Oct 12 '19 at 17:34
  • In XCode 11, I prefer running the Extension target and then Selecting the client app because the "Choose and app to run" picker is much more helpful than the "Debug->Attach to Process" menu. – paiego Apr 24 '20 at 02:03
44
  1. Run service extension as the Target instead of the app. Then it will ask for which app you have run service extension, then select your app and it will send the notification.

  2. Make sure the deployment target of the service extension is less than your physical device's OS version.

  3. Ensure payload contains 'mutable-content: 1'

{"aps" : {
    "alert" : {
        "title" : "Introduction To Notification",
        "subtitle" : "Session 707",
        "body" : "New Notification Look Amazing"
    },
    "sound" : "default",
    "category" : "message",
    "badge" : 1,
    "mutable-content": 1
    },
    "attachment-url": "https://api.buienradar.nl/Image/1.0/RadarMapNL"
}
  1. Don't add the content-available flag in aps or if you've added then make sure it's set to 0.

---------------------------------------- if nothing works, Restart your device --------------------------------------

Sudhanshu Gupta
  • 2,255
  • 3
  • 36
  • 74
  • 3
    Running the Extension as the target is the key! Took me a while to figure this one out. Thanks for pointing it out! – kakubei Apr 05 '17 at 11:09
  • 3
    2. saves me. Thanks :) – iVarun Jan 06 '18 at 07:31
  • 1
    my notifications { "registration_ids": ["devicetoken"], "mutable_content": true, "data": { "post_details": { "pcm_message": "asdf", "pcm_img_url": "portalvh/15683.jpg";, } }, "notification" : { "title" : "demo push", "body" : "this is push body" } } but still one don't call Notification Service Extension where is problem or missing some information I have already set deployment target 10.0 in my whole project – Ankur Patel Feb 28 '18 at 10:43
  • 4
    I don't see the point of omitting `content-available`. We send both flags and it works fine. – kas-kad Mar 22 '18 at 07:44
  • 1
    This finally solved it for me. I've seen several posts about attaching to the Service Extension after running the app, but that would not work. This worked like a charm! – coping Jul 14 '19 at 22:27
  • 1
    @kas-kad you shouldn't. From _WWDC 2017 - Best Practices and What’s New in User Notifications_: "[If instead you want to launch your application in the background and run some additional processing, you should send a silent notification.](https://developer.apple.com/videos/play/wwdc2017/708/?time=1410)" it would confuse the OS as to who wants to handle the payload. `content-available: 1` means let the app handle. `mutable-content` means let the service extension handle it. – mfaani Jun 05 '20 at 23:17
  • @Honey I'm sure the OS is smart enough to figure out :) from my experience, first goes the extension handling the message, the app follows in case it is not killed. – kas-kad Jul 22 '20 at 15:44
10

I was getting crazy. Finally I realized that I had the deployment target of Notification Service Extension was 10.3 (my phone too). I changed to 10.2 and it works perfectly

croigsalvador
  • 1,993
  • 2
  • 24
  • 47
8

If all else fails, restart your device. I just spent 2 hours on this and simply restarting my phone fixed it.

chopsalot
  • 334
  • 4
  • 10
  • 2
    You sir, deserve my upvote, thank you! Apparently it was necessary to kill the service extension previously installed with the wrong deployment target. – ncuillery Dec 20 '20 at 15:13
  • Mark me down for 2 hours spent as well. – skg Apr 26 '21 at 07:45
4

Your push notification payload should contain the "mutable-content" : 1 key value pair.

The remote notification’s aps dictionary includes the mutable-content key with the value set to 1.

Ex of push notification payload JSON:

{
  "aps":{
          "alert": {
                    "body": "My Push Notification", 
                    "title" : "Notification title"},
          "mutable-content" : 1,
          "badge":0},
}

This works perfectly fine and i get the Push notification as follows: Push Notification

Also Note That :

You cannot modify silent notifications or those that only play a sound or badge the app’s icon.

You can try Pusher or Houston for testing the Push Notifications.

Raja Vikram
  • 1,970
  • 15
  • 17
3

I have the same issue but my problem was notification extension is "app". It should be appex

V V
  • 774
  • 1
  • 9
  • 29
  • 1
    This. My project has multiple targets & I use .xcconfig files to share build settings across all similar targets. Creating the service extension from the template does not set the Wrapper Extension to `appex`, so it was picking up the `app` extension from the project. – Jordan Sep 19 '18 at 23:32
3

If you trying to send push from Firebase console it's not possible to send "mutable_content": true. So you should send it from server or postman or any type of terminal. And your payload should be like this: For firebase

{
    "to" : "your device token/ firebase topic",
    "notification" : {
      "body" : "This is an FCM notification that displays an image.!",
      "title" : "FCM Notification"
     },
    "mutable_content": true
  }

If not from FCM then your payload should be:

{"aps" : {
    "alert" : {
        "title" : "Your title",
        "subtitle" : "Your subtitle",
        "body" : "Your body"
    },
    "sound" : "default",
    "category" : "message",
    "badge" : 1,
    "mutable-content": 1
    },
    "attachment-url": "https://yourattachment-url.com"
}

Note: And make sure the deployment target of the service extension is less that your physical device's OS version. Like if your development target is 10.3, your service extension version should be 10.2.

Jamil Hasnine Tamim
  • 4,389
  • 27
  • 43
2

From docs on UNNotificationServiceExtension class:

  • The remote notification is configured to display an alert.

  • The remote notification’s aps dictionary includes the mutable-content key with the value set to 1.

You cannot modify silent notifications or those that only play a sound or badge the app’s icon.

Basically

Must include:

  • mutable-content: 1
  • an alert dictionary.

Must NOT include:

  • content-available: 1

To summarize Apple is doing its best to not allow apps to mutate silent notifications. It wants to allow that only on user notifications (user facing notifications)

Community
  • 1
  • 1
mfaani
  • 33,269
  • 19
  • 164
  • 293
0

you need to add "mutable-content": 1 to your payload

Ryan110
  • 710
  • 4
  • 19
0

This is a sample if you implement with Swift and Firebase cloud functions (Node.js) to send notifications has attachments with Notification Service Extension. Below i have mentioned some important points only.

Payload sample

const payload = {
    notification: {
        title: name,
        body: messageText,
        badge: "1",
        mutable_content: "true"
    },
    data: {
        type: "MESSAGE",
        fromUserId: name,
        attachmentUrl: imageUrl
    }};

mutable_content: "true" this part is the important i spend so much of time to find the exact naming most of the documentations are invalid now.

The next main thing is the way we have to run the App

Select the Notification Service Extension with your device enter image description here

After when you run you will get a popup to Select your main project

This way it will call the NotificationService didReceive function (Which is inside the Notification Service Extension) when you receive a notification.

Lahiru Pinto
  • 1,621
  • 19
  • 20