DidReceiveRemoteNotification never called in Xamarin.iOS - 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.

Related

Register for notifications (FCM) outside AppDelegate

I want to ask the user to allow notifications only when we prompt the "AskNotification" view and when he click on "Yes".
In order to do that, I did the following :
public static AppDelegate Self { get; private set; }
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
HtmlLabelRenderer.Initialize();
global::Xamarin.Forms.Forms.Init();
// Notifications
Firebase.Core.App.Configure();
//AllowNotifications();
...
LoadApplication(new App());
AppDelegate.Self = this;
return base.FinishedLaunching(app, options);
}
public void AllowNotifications()
{
//In iOS you must request permission to show local / remote notifications first since it is a user interrupting action.
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
// Request Permissions
UNUserNotificationCenter.Current.RequestAuthorization(
UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound,
(granted, error) =>
{
// Do something if needed
});
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.Current.Delegate = this;
// For iOS 10 data message (sent via FCM)
Messaging.SharedInstance.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);
}
Messaging.SharedInstance.ShouldEstablishDirectChannel = true;
Console.WriteLine("-------- RegisterForRemoteNotifications");
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
And then on my Code Behind when the user click on the "Allow" button (from my view) I do the following :
AppDelegate appDelegate = AppDelegate.Self;
appDelegate.AllowNotifications();
As you can see, I'm using the Singleton pattern to have an access to the AppDelegate. My problem is when the "AllowNotifications" is called inside the AppDelegate (it's commented on the code above), the system prompt ask for the user and notifications are received.
But when I call the AllowNotification method from another page with the Singleton pattern. The system popup is showing, we the user click "Yes" it allow notification on iOS parameters. But I never get into my "DidReceiveMessage" method.
Thanks for your help
This issue has nothing to do with your code.It is an expected behavior.Because you used firebase (Xamarin.Firebase.iOS.CloudMessaging from NuGet).
For devices running iOS 10 and above, you must assign your delegate object to the UNUserNotificationCenter object to receive display notifications, and the FIRMessaging object to receive data messages, before your app finishes launching. For example, in an iOS app, you must assign it in the method FinishedLaunching.
That is to say,when you register for notifications out the method FinishedLaunching,even if system popup showed,the register will still not working.
For more detail you can refer here.

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.

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

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?

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