Push Notification Delivery and Dismiss Callback - ios

Can we make a service call from the client side when a push notification is delivered or dismissed when the app is not active?
Also does APNS give any information on whether the notification is delivered at an individual level and is there any api we can use to query the delivery status report?

Yes you can detect delivery of remote notification if you send the content-available flag to 1 in the aps dictionary. If you set the key, application:didReceiveRemoteNotification:fetchCompletionHandler: method of the AppDelegate is called.
Also, from iOS 10 onwards, you can also detect dismissal of remote notifications. For that you need to implement UserNotifications Framework.
You need to perform the below steps for the same:
Use iOS 10 APIs to register the categories for remote notifications.
You need to check [[UNUserNotificaionCenter currentNotificationCenter] setNotificationCategories] method for the same. See https://developer.apple.com/documentation/usernotifications/unusernotificationcenter
iOS 10 introduces a new class called UNNotificationCategory for encapsulating a category instance. You need to set the CustomDismissAction on the category instance to be able to receive the dismiss callback.
See https://developer.apple.com/documentation/usernotifications/unnotificationcategory
Implement the UNUserNotificationCenterDelegate protocol with the implementation of userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: method. This method will receive the callback to the dismiss action provided you perform the above steps.
See https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate
Set your delegate to the UserNotificationCenter - [[UNUserNotificaionCenter currentNotificationCenter].delegate = your_delegate_implementation
Set the category with CustomDismissAction property in the payload.
{
"aps": {
"alert": "joetheman",
"sound": "default",
"category": "my-custom-dismiss-action-category",
"content-available":1
},
"message": "Some custom message for your app",
"id": 1234
}

Yes you can make a service call from the ios app when a notification is received when app is not active. Do this in:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
<#code#>
}
yes you can add additional key value pair in your push notification payload.
{
"aps": {
"alert": "joetheman",
"sound": "default"
},
"message": "Some custom message for your app",
"id": 1234
}

Related

Notification delegate not being called when app is in background when notification is received

I am using firebase cloud messaging to send push notifications to our iOS application. When the application is in the foreground everything works fine.
The problem is when i send the app to the background and then send a notification.
I expect the following delegate to be called, but it is not:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult)-> Void) {
I have ticked the "remote notifications" background mode in my apps signing & capabilities tab.
The payload i send to firebase looks like this:
{"to":"--TOKEN--",
"priority":"high",
"content_available":true,
"mutable_content": true,
"notification":
{"body":"Test Notification",
"title":"New Notification",
"sound":"default",
"click_action":"notification"
}
}
This payload is sent to firebase via https://fcm.googleapis.com/fcm/send
When i click an notification it is then processed, and i can see the apns version i receive looks like this:
[AnyHashable("google.c.sender.id"): 8xxxxxxxx, AnyHashable("gcm.message_id"): 1xxxxxxxxxx, AnyHashable("google.c.a.e"): 1, AnyHashable("google.c.fid"): fxxxxxxxxx, AnyHashable("aps"): {
alert = {
body = "Test Notification";
title = "New Notification";
};
category = feedback;
"content-available" = 1;
"mutable-content" = 1;
sound = default;
}]
I am not sure why content-available and mutable-content appear in quotes? I know firebase convers its payload to apns format, is there something wrong here?
I want the delegate to be called so that i can execute some code to maintain various data items, so it is important that i can run some code when my app is in the background and a notification is received.
I am not sure what config i am missing as everything i read seems to say this is all i need to do?

Push notifications on Swift

I have a really big problem with Firebase Notifications;
my problem is that i can receive notification in background mode and foreground mode, but if I terminate it(kill it from the ram) I can't receive anything.
I found that if I comment this function
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
}
I can receive a notification when i reopen the application after it's terminated, but i can't get a notification in background like before.
There's any way to solve it, because my application should receive notification everytime: background, foreground and when application is reopened from a terminated state
The following is an example of my notification's JSON:
{
"content_available": true,
"priority": "high",
"data": {
"priority": "SILENT",
"target": "contact",
"msgBody": "",
"msgTitle": ""
},
"to": "firebase_TOKEN"
}
I also want to say that this notification should be silent
When app is killed notification will be handled from operating system so it needs some specific key to display notification.
Like consider the below example:
{
"content_available": true,
"notification": {
"title": "has sent you a message",
"sound": "default",
"body": "Hi",
"badge": 6
},
"to": "firebase_TOKEN",
"priority": "high"
}
Here you need to replace "data" with "notification" and you also need a "title" instead of "msgTitle" and "msgBody" will be replaced with "body".
Looks like your's is a android payload which won't work with iOS.
You must required to add "Content-available" : "1", in your notification to get push notification when application killed.

didReceiveRemoteNotification: is not called after notification arrives and app is in background/suspended mode

Within AppDelegate I simply update applicationIconBadgeNumber:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
application.applicationIconBadgeNumber += 1
}
Everything works as expected when the app is connected to Xcode and is in debugger mode. But just after I plug it out from Xcode, notification arrives but badge is not updated. App is in background mode.
Why? What is wrong with my approach? Please, give me an advice🏆
Push notification are handled by iOS and not your app you can't change the application badge on receiving a push notification.
You can send the badge number in the payload of the push notification,
Payload could look like this:
{
"aps" : {
"alert" : "Notification REceived",
"badge" : 1
}
}
I found this post very helpful for managing notifications in the background, basically you have to use the call with completion handler.
didReceiveRemoteNotification when in background
I think the best way to manage badge count is on the server.
A push notification will automatically update the badge on the app icon.
Payload formate.
{
"aps" : {
"alert" : "Weather report udapted",
"badge" : 5
}
}
Your messagingManager is an optional. You should check, that it isn't nil. If it is nil, the appDidReciveMessage() function would not be triggered

iOS 10 don't call Notification Service Extension

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?
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
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.
Make sure the deployment target of the service extension is less than your physical device's OS version.
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"
}
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 --------------------------------------
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
If all else fails, restart your device. I just spent 2 hours on this and simply restarting my phone fixed it.
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:
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.
I have the same issue but my problem was notification extension is "app". It should be appex
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)
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.
you need to add "mutable-content": 1 to your payload
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
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.

Parse and Swift: Push notifications with "aps": {"content-available": 1} not being received by didReceiveRemoteNotification

I making a Parse REST API request like this
curl -X POST \
-H "X-Parse-Application-Id: key-here" \
-H "X-Parse-REST-API-Key: key-here" \
-H "Content-Type: application/json" \
-d '{
"where": {
"deviceToken": "token-here",
"deviceType": "ios"
},
"data": {
"aps": {"content-available": "1"}
}
}' \
https://api.parse.com/1/push
Now, this gets read by the device, when the app is turned on. But when i lock the device and the make the above curl request again, nothing happens. Below is my didReceiveRemoteNotification function.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
println("received notification")
var remoteServer = RemoteServer()
remoteServer.sendDataToServer { (success) -> () in
completionHandler(UIBackgroundFetchResult.NewData)
}
}
As soon as I, open the app the inside call of didReceiveRemoteNotification gets excuted. but again, nothing happens when the app is not open or phone is locked.
Any idea/suggestions here?
I assume that you configure all the certificates and provisioning profile correctly. because as you said you are receiving the Response when app is in Active state (in your words its Turned on)
But when app is in background mode or when you lock the device at that time your app's status is not active. and you are not receiving the Alerts or Sound for push notification.
This happens because of PAYLOAD which contains information about how the system will alert the user OR handle the data you sent.
for each notification, you need to compose a JSON dictionary object which contain another directory named "aps"
aps contain following :
An alert message (which will displayed to user) [String OR
Dictionary]
A number to badge (it is displayed on the top right corner of App icon.) [number]
A sound to play (it plays the sound) [String] here you need to pass the name of sound file in app bundle
If you want to show the alert or banner on your device Notification Panel or on the lock screen, then your aps must have the proper values according to their types.
And one more thing
"aps": {"content-available": "1"}
your aps contain the above JSON.
content-available This property with value 1 Means : Remote notification work as SILENT NOTIFICATION And when SILENT
NOTIFICATION kind of notification arrives iOS activce your app in
background mode from which you can complete you background progress.
(For ex : Downloading the data or other process)
here i provide the sample example of aps dictionary.
A aps JSON example which include "alert, sound and badge" ia as below :
"aps" : {
"alert" : "This a alert",
"badge" : 1,
"sound" : "bingbong.aiff"
}
And make sure you have configure for the remote-notifcation correctly in your AppDelegate class.
For iOS7 and older (in swift)
var types = UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound | UIRemoteNotificationType.Alert
UIApplication.sharedApplication().registerForRemoteNotificationTypes(types)
For iOS8
var notiType = UIUserNotificationType.Badge | UIUserNotificationType.Sound | UIUserNotificationType.Alert
var notiSettings:UIUserNotificationSettings
notiSettings = UIUserNotificationSettings(forTypes:notiType, categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notiSettings)
UIApplication.sharedApplication().registerForRemoteNotifications()
Hope It helps.

Resources