How to get when Local Notification is fired on iOS 10+ - ios

I am using Xamarin.iOS and I know that ReceivedLocalNotification is deprecated on iOS 10+
So I implemented:
UNUserNotificationCenter.Current.Delegate = new UserNotificationCenterDelegate();
Which leads to following:
public class UserNotificationCenterDelegate : UNUserNotificationCenterDelegate {
public UserNotificationCenterDelegate() { }
public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
completionHandler(UNNotificationPresentationOptions.Alert);
}
public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
....
}
}
I am using iOS 10/ iOS 11 and the Local Notification fires in background but I am not able to get the event.
Is this even possible to get the event when Local Notification is fired without the user interacting with it?

Related

Handle local notification tap Xamarin Forms iOS

I have created an application on Xamarin Forms in which I am scheduling local notifications in PCL through a Plugin named Plugin.Notifications. But I wanted to know whether the user enters into the app through notification or not.
I tried to check whether the UIApplication.LaunchOptionsLocalNotificationKey is present in the launch 'options' dictionary or not, but the launch options dictionary is always null.
Then I tried to handle it through the ReceivedLocalNotification delegate method, and I was able to get the tap event, it works fine when the app is in foreground or in the background, but when the app gets killed and opens through tapping on the notification, its getting crashed.
I am unable to find the issue for the crash.
here is what I am doing in the ReceivedLocalNotification method.
I am setting a bool value through a DependencyService.
public override void ReceivedLocalNotification(UIApplication application, UILocalNotification notification)
{
NSUserDefaults.StandardUserDefaults.Init();
DependencyService.Get<INotificationTap>().SetNoitificationTap(true);
}
The handling of the Dependency Service
[assembly: Xamarin.Forms.Dependency(typeof(NotificationTapIOS))]
namespace abc.iOS
{
public class NotificationTapIOS:NSObject,INotificationTap
{
public bool GetNotificationTap()
{
return NSUserDefaults.StandardUserDefaults.BoolForKey("notificationTapKey");
}
public void SetNoitificationTap(bool isNotificationTapped)
{
NSUserDefaults.StandardUserDefaults.SetBool(isNotificationTapped,"notificationTapKey");
NSUserDefaults.StandardUserDefaults.Synchronize();
}
}
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
if (options != null)
{
// check for a local notification
if (options.ContainsKey(UIApplication.LaunchOptionsLocalNotificationKey))
{
var localNotification = options[UIApplication.LaunchOptionsLocalNotificationKey] as UILocalNotification;
if (localNotification != null)
{
LoadApplication(new App());
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;// reset our badge
}
}
}
else
{
LoadApplication(new App());
}
}
I found a workaround for this issue, I don't know whether this is a correct approach, but it worked for me.
In Xamarin.iOS , when the app launched , the base class methods are called first(i.e applicationDidFinishLaunching) and then the Xamarin part(i.e FinishedLaunching) is called.
So I changed the FinishedLaunching parameters names , similar to the base class parameters (i.e 'app' to 'uiApplication' and 'options' to 'launchOptions' ) and I got the key in the launchOptions Dictionary.
public override bool FinishedLaunching(UIApplication uiApplication, NSDictionary launchOptions)
{
global::Xamarin.Forms.Forms.Init();
isNotificationTapped = false;
if (launchOptions != null) {
if (launchOptions.ContainsKey(UIApplication.LaunchOptionsLocalNotificationKey))
isNotificationTapped = true;
}
LoadApplication(new App());
return base.FinishedLaunching(uiApplication, launchOptions);
}
When the user starts the app through notification tap, then FinishedLaunching method of AppDelegate is executed and we can validate the launchOptions dictionary to find the tap event, and if the app is in background or running stage , and user taps on the notification then the RecieveLocalNotification method of the AppDelegate is called.
public override void ReceivedLocalNotification(UIApplication application, UILocalNotification notification)
{
isNotificationTapped = true;
}
After initializing the value , I saved it in the UserDefaults using the DependencyService

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 remote notifications do not trigger when in background mode

I do not usually post here, but this time I am stuck. I searched already on this forum, on stackoverflow and on google in general but I cannot understand what I am doing wrong.
I am developing a Xamarin.Forms app and I am having trouble to fire the methods when a remote notification is received.
Here is my code:
Just after login I execute this method to register to apple notification center:
public void RegisterForNotifications ()
{
var settings = UIUserNotificationSettings.GetSettingsForTypes(
UIUserNotificationType.Alert
| UIUserNotificationType.Badge
| UIUserNotificationType.Sound,
new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
this call successfully trigger this method on AppDelegate
public override void RegisteredForRemoteNotifications (UIApplication application, NSData deviceToken)
{
var azureService = App.GetBackEndService ();
azureService.RegisterForNotifications (deviceToken);
}
RegisterForNotifications registers with azure notification hub:
public static async void RegisterForNotifications(this BackEndService service, NSData deviceId)
{
Microsoft.WindowsAzure.MobileServices.MobileServiceClient client = new Microsoft.WindowsAzure.MobileServices.MobileServiceClient (service.Client.ApplicationUri);
client.CurrentUser = service.Client.CurrentUser;
var push = client.GetPush ();
var tags = new List<string>() { App.GetBackEndService().Client.CurrentUser.UserId };
try{
await push.UnregisterAllAsync(deviceId);
}catch(Exception e){
var exp = e.Message;
}
await push.RegisterTemplateAsync(deviceId, NOTIFICATION_TEMPLATE, "", "NotificationTemplate", tags);
}
Now, when I send a notification and the app is on foreground the method triggered is this one (In AppDelegate):
public override void ReceivedRemoteNotification (UIApplication application, NSDictionary userInfo)
{
ShowAlerts (userInfo);
}
But when in background mode (for example when I press the Home button on the Iphone) nothing gets triggered
And nothing shows on the upper bar where the iOS notifications are usually shown.
I placed a breakpoint in ReceivedRemoteNotification, DidReceiveRemoteNotification and also on FinishedLaunching.
I properly set Remote notifications in Info.plist under Enable Background Modes
Can someone understand what I am missing?

Remote Notifications with background update

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);
}

Resources