Remote Notifications with background update - ios

I'm implementing Remote Notifications and I'm trying to update the app before the notification is shown to the user.
The problem is that the notification is shown before I finish updating the data, what am I missing?
public override void DidReceiveRemoteNotification (UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
//Some code to update the data - the notification is shown before this executed.
completionHandler (UIBackgroundFetchResult.NewData);
}

Related

How to delete specific remote notification if app is closed or in background

My app has a chat service, when new notification is received, I want to clear the notifications between user1 and user2 except the new one.
I can do it when app is in foreground by calling:
UNUserNotificationCenter.current().getDeliveredNotifications { notifications in
print("count: \(notifications.count)")
for notif in notifications {
let nUserInfo = notif.request.content.userInfo
let nType = Int(nUserInfo[AnyHashable("type")] as! String)
if nType == type {
let notifId = notif.request.identifier
if notifId != notification.request.identifier {
center.removeDeliveredNotifications(withIdentifiers: [notif.request.identifier])
}
}
}
where type is a customValue.
How to do this when app is in background or closed by a user.
You need to turn on Background Modes capability and check Remote notifications mode. In order to delete the notification in background, you need to send a new notification with no alert, like {"aps": {"content-available": 1}, "del-id": "1234"}, where content-available means (you can check more about here Apple push service)
Include this key with a value of 1 to configure a background update notification. When this key is present, the system wakes up your app in the background and delivers the notification to its app delegate. For information about configuring and handling background update notifications, see Configuring a Background Update Notification.
and del-id will be the id of the notification you want to delete, you can use an array as well. You can put these information with together with your message notification as well.
In your AppDelegate.swift you will need to add this method to delete the notification in background. In your case, you can send the id of the notification you does not want to delete and use your method to delete all delivered notification except the one with the id you send in your last notification.
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
guard let idToDelete = userInfo["del-id"] as? String else {
completionHandler(.noData)
return
}
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [idToDelete])
completionHandler(.noData)
}

iOS RemoveDeliveredNotifications(string[] identifier) will not delete the notification(s) when app in background

maybe someone can help me.
In my app I'm using push notifications to inform the users that a new message is written to the database. One user can accept the notification and work with the content or dismiss it. If the user accepts it, a silent push is sent to all other devices which received the notification earlier. Here is my code handling this silent notification:
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary remoteNotification)
{
try
{
if (remoteNotification != null)
{
var alert = remoteNotification[FromObject("aps")];
if (alert != null)
{
string id = ((NSDictionary)alert)[FromObject("deleteId")].Description;
if (!String.IsNullOrEmpty(id))
{
List<string> idents = new List<string>();
UNUserNotificationCenter.Current.GetDeliveredNotifications(completionHandler: (UNNotification[] t) =>
{
foreach (UNNotification item in t)
{
UNNotificationRequest curRequest = item.Request;
var notificationId = ((NSDictionary)curRequest.Content.UserInfo[FromObject("aps")])[FromObject("notificationId")].Description;
if (id == notificationId)
{
idents.Add(curRequest.Identifier);
}
}
UNUserNotificationCenter.Current.RemoveDeliveredNotifications(idents.ToArray());
});
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
The problem is that the notification is still visible in the notification center until the app is brought to foreground. But then it gets deleted.
Is there a way to force the method to delete the notification instantly and not only when the app is (re)opened?
When you want to clear the Notifications send from this app. Set its application's badge to 0 to achieve this.
As you said you send a silent notifications to other users, Then DidReceiveRemoteNotification() will fire. In this event we can clear all notifications:
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
var aps = userInfo["aps"] as NSDictionary;
if (aps["content-available"].ToString() == "1")
{
//check if this is a silent notification.
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
}
completionHandler(UIBackgroundFetchResult.NewData);
}
Please notice that starting with iOS 8.0, your application needs to register for user notifications to be able to set the application icon badge number. So please add the code below in FinishedLaunching():
UIUserNotificationSettings settings = UIUserNotificationSettings.GetSettingsForTypes(UIUserNotificationType.Badge, null);
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
Moreover silent notifications can only be received when your app is on background or foreground. If it's terminated, this will fail.
To remove a notification, you send a silent push notification to all devices with the notification ID as payload that should be removed.
On the clients you implement a UNNotificationServiceExtension which allows you to remove currently displayed notifications by their IDs: UNUserNotificationCenter.current().removeDeliveredNotifications.
This gives you the advantage that you have full control over this logic on the server side.

DidReceiveRemoteNotification never called in Xamarin.iOS

I'm trying to implement push notifications for my iOS application using Xamarin and the Xamarin.Firebase.iOS.CloudMessaging package.
I've got everything setup and working i,e. When the application is in the foreground I can receive notifications (containing and not containing the "content-available" tag) and interact with them (tapping the notification etc.)
However, when the application is in the background, I receive the notifications but, no callbacks are called. To my understanding, the "didReceiveRemoteNotification" should be called when:
The application is in the foreground regardless of if the "content-available" tag is enabled
If the application is in the background or suspended, with the "content-available" tag enabled
When the user taps on the notification
Here are the functions I've implemented in my AppDelegate.cs:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
Firebase.Core.App.Configure();
FireBaseRegistration();
return base.FinishedLaunching(app, options);
}
private void FireBaseRegistration()
{
// Register your app for remote notifications.
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
// iOS 10 or later
var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) => {
Console.WriteLine(granted);
});
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.Current.Delegate = this;
}
else
{
// iOS 9 or before
var allNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound;
var settings = UIUserNotificationSettings.GetSettingsForTypes(allNotificationTypes, null);
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
}
//Register for APNs notifications
UIApplication.SharedApplication.RegisterForRemoteNotifications();
Messaging.SharedInstance.Delegate = this;
var token = InstanceId.SharedInstance.Token;
Debug.WriteLine(token);
//////Connect to FCM (Only used for Foreground notifications)
Messaging.SharedInstance.ShouldEstablishDirectChannel = true;
// Monitor token generation
InstanceId.Notifications.ObserveTokenRefresh((sender, e) => {
// Note that this callback will be fired everytime a new token is generated, including the first
// time. So if you need to retrieve the token as soon as it is available this is where that
// should be done.
token = InstanceId.SharedInstance.Token;
Console.WriteLine(token);
});
}
//Register APNs token because method swizzling is de-activated
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
Console.WriteLine("Registered for remote notifications");
Console.WriteLine(deviceToken.GetBase64EncodedString(NSDataBase64EncodingOptions.None));
Console.WriteLine(deviceToken);
}
[Export("messaging:didReceiveMessage:")]
public void DidReceiveMessage(Messaging messaging, RemoteMessage remoteMessage)
{
// Do your magic to handle the notification data
Console.WriteLine("iOS 11 Foreground");
}
//Shows local notification and is called when user taps notification
[Export("userNotificationCenter:DidReceiveRemoteNotification:withCompletionHandler:")]
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
Console.WriteLine("Received a notficaiton");
completionHandler(UIBackgroundFetchResult.NewData);
}
//To receive notifications in foreground on iOS 11 devices.
[Export("userNotificationCenter:willPresent:withCompletionHandler:")]
public void WillPresent(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
Console.WriteLine("Handling iOS 11 foreground notification");
completionHandler(UNNotificationPresentationOptions.Sound | UNNotificationPresentationOptions.Alert);
}
////Called when tapping notification
[Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
Console.WriteLine("Handling push notificaiton interaction");
completionHandler();
}
//Receive data message on iOS 10 devices.
public void ApplicationReceivedRemoteMessage(RemoteMessage remoteMessage)
{
Console.WriteLine("Handling iOS 10 data message notification");
}
//// To receive notifications in foreground on iOS 10 devices.
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
Console.WriteLine("Handling foreground notification");
completionHandler(UNNotificationPresentationOptions.Alert);
}
I've tried these out on iOS 10.3.3 and iOS 11.2. Here are the functions called for both versions:
When the application is in the foreground
On iOS 10.3.3: WillPresentNotification() (with and without the "content-available" tag)
On iOS 11.2: WillPresentNotification() (with and without the "content-available" tag)
When the application is in the background:
On iOS 10.3.3: nothing Shoudln't it be DidReceiveRemoteNotification?
On iOS 11.2: nothing Shoudln't it be DidReceiveRemoteNotification?
I have the remote-notificaitons and background fetch background modes activated in my Info.plist file.
Packages info:
Xamarin.forms: 2.5.0.280555
Xamarin.Firebase.iOS.CloudMessage: 2.0.4.1
Xamarin.Firebase.iOS.Core: 4.0.13
Xamarin.Firebase.iOS.InsstanceID: 2.0.8
Remarks:
The DidReceiveMessage function is implemented in the Xamarin sample but, in my case, is never called.
The DidReceiveNotificationResponse is called when the user taps on the notificaiton.
The WillPresent is also never called.
I've tried with notifications via the firebase API and directly via the APNs (with the pusher application).
Set Messaging.SharedInstance.ShouldEstablishDirectChannel=false and send data-only messages.

iOS Swift Twilio Programmable Chat Push Notifications

Using the TwilioChatClient pod, I have successfully registered my app to Twilio Programmable Chat to receive APN notifications.
However, from what I can tell, these notifications are being created after calling client.register(withToken: deviceToken) on an instantiated TwilioChatClient client, and NOT through the application's AppDelegate didReceiveRemoteNotification method. Stranger yet, didReceiveRemoteNotification is called, but only when the application is in the active state, and not the background state, where I would like to perform some operations.
Does anyone know where and how these notifications are being created, or why didReceiveRemoteNotification is only called during the active state? Amongst other things, I would like to increment the application icon badge number with each notification sent out by the client.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("Registered for notifications");
if UserUtils.client?.userInfo != nil {
print("Has info");
UserUtils.deviceToken = deviceToken;
UserUtils.client?.register(withToken: deviceToken)
} else {
print("No info");
updatedPushToken = deviceToken as NSData?
}
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("Received a notification");
if UIApplication.shared.applicationState != .active {
print(userInfo);
UserUtils.client?.handleNotification(userInfo);
UIApplication.shared.applicationIconBadgeNumber += 1;
if UserUtils.client?.userInfo != nil {
print(userInfo);
let jsonNotification = JSON(userInfo["aps"])
let alert = jsonNotification["alert"].stringValue + "\"}";
print(JSON.init(parseJSON: alert)["body"]);
} else {
print(userInfo);
let jsonNotification = JSON(userInfo["aps"])
let alert = jsonNotification["alert"].stringValue + "\"}";
print(JSON.init(parseJSON: alert)["body"]);
}
} else {
}
}
where the client.register(withToken: deviceToken) works as intended.
Twilio developer evangelist here.
I've spoken with the Programmable Chat team and this is what I've found out:
application(_:didReceiveRemoteNotification:fetchCompletionHandler:) is for silent notifications in the background that perform background processing only (that is, with "content-available": 1 set in the APNS notification). Programmable Chat sends notifications that show information to the user, so it won't be fired in background mode
Notifications can update the badge count for you though, so this is processing you don't have to do, this requires a different setting in the notification that we currently do not support, however work is being done to add that support now
If you want to both show a notification and do further background processing, this is not supported in regular notifications, however this is supported with iOS 10's service extensions. Programmable Chat doesn't support those either, but again, it is being worked on, so you may see that soon
Keep an eye on the Programmable Chat Changelog for these additions.
Let me know if that helps at all.

Handling push notifications payload data

I am searching the way about how to handle push notification payload data as soon as the notification reaches to the client app without opening or tapping it.And I am still not getting the data unless the user tap or open it from notification center or banner or alert.The function didReceiveRemoteNotification only triggered when the user click the notification on the screen.So,how to get the notification payload data when the notification arrive to client app even the user ignore(without open or tap) it.
INFO : I heard that GCM(Google Cloud Messaging) can make notification handler if the client app user tapped the notification or not.It can catch the notification payload json data as soon as it reach the client app without even need user to tap or open it.Is that right?
I really need a hand to pick me up with getting notification payload data on ios without even need a user to open or tap it.
Update : The app is still running on device which mean it was active.I can get the payload data when i click my notification which was "{aps:} json i get it.But,I still cant get the data when i don't open the notification"
Here is my state
When the app was at foreground,I get the data.
1.I run the App,
2.Send Notification,
3.Get the notification with an alert,
4.I get the data(payload).
Work fine when app is active.
But,when the app reach to background
1.Run The app,
2.Close The App by pressing home button,
3.Send Notification,
4.Get the notification.
5.But,cant get the data until i click notification that I was receive at banner
or notification center.
But,when i click the notification at banner or notification it went to app and then i get the data.
Is there anyway that i can get the data if the app in background when the notification received.
Here is the code :
import UIKit
import RealmSwift
let realm = try! Realm()
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var data : [NSObject : AnyObject]!
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
//one singnal is the push notification service that i use for push notification.
let oneSignal = OneSignal(launchOptions: launchOptions, appId: "__app_id__") { (message, additionalData, isActive) in
NSLog("OneSignal Notification opened:\nMessage: %#", message)
if additionalData != nil {
NSLog("additionalData: %#", additionalData)
}
}
oneSignal.enableInAppAlertNotification(true)
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
print("User Info : \(userInfo)")
if let custom = userInfo["custom"] as? NSDictionary{
if let a = custom["a"] as? NSDictionary{
print("A : \(a)")
}
}
}
I came across the same problem. As mentioned in the previous comments, this post is quite helpful.
According to Apple,
When a remote notification arrives, the system displays the
notification to the user and launches the app in the background (if
needed) so that it can call this method. Launching your app in the
background gives you time to process the notification and download any
data associated with it, minimizing the amount of time that elapses
between the arrival of the notification and displaying that data to
the user.
The first thing you have to do is to allow your app to do something when in background. You do this by adding Required background mode in your info.plist, then add it App downloads content in response to push notifications. Your info.plist should look something like this:
Now this is done, your app should awake when it receive a notification. You can execute a small code inside didReceiveRemoteNotification. Such as this.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
//do some code here
UIBackgroundFetchResult.NewData
}
Note that you have to pay attention to the completionHandler:
As soon as you finish processing the notification, you must call the
block in the handler parameter or your app will be terminated. Your
app has up to 30 seconds of wall-clock time to process the
notification and call the specified completion handler block.
Let me know if everything is clear :)

Resources