Xamarin Firebase for iOS giving fcm 501 error - ios

I have been struggling with this for several days and have found many other posts on the subject, but nothing seems to fix the problem I have. I have a feeling this might be simple as I have tried this several times with cut down projects and always get the same failure. I can receive Firebase messages fine in Android, but iOS consistently defeats me.
I am using a phone with iOS 10 as a test. The debug code always gets a token back, but on connecting, an error 'The operation couldn’t be completed. (com.google.fcm error 501.)' is returned. I attach some sample code - this has been bodged around in frustration
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IUNUserNotificationCenterDelegate, IMessagingDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init ();
LoadApplication (new FirebaseTest.App (""));
RegisterForNotificationFCM();
return base.FinishedLaunching (app, options);
}
void RegisterForNotificationFCM()
{
// 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;
// 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);
}
UIApplication.SharedApplication.RegisterForRemoteNotifications();
Firebase.Analytics.Loader loader1 = new Firebase.Analytics.Loader();
Firebase.InstanceID.Loader loader2 = new Firebase.InstanceID.Loader();
//Firebase.Core.App.Configure();
Firebase.InstanceID.InstanceId.Notifications.ObserveTokenRefresh((sender, e) =>
{
var newToken = Firebase.InstanceID.InstanceId.SharedInstance.Token;
System.Diagnostics.Debug.WriteLine(newToken);
connectFCM();
});
}
public override void DidEnterBackground(UIApplication uiApplication)
{
Messaging.SharedInstance.ShouldEstablishDirectChannel = false;
}
public override void OnActivated(UIApplication uiApplication)
{
base.OnActivated(uiApplication);
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
Messaging.SharedInstance.ApnsToken = deviceToken;
connectFCM();
//Firebase.InstanceID.InstanceId.SharedInstance.SetApnsToken(deviceToken, Firebase.InstanceID.ApnsTokenType.Prod);
}
public void DidRefreshRegistrationToken(Messaging msg, string str)
{
}
//Fire when background received notification is clicked
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
//Messaging.SharedInstance.AppDidReceiveMessage(userInfo);
System.Diagnostics.Debug.WriteLine(userInfo);
// Generate custom event
NSString[] keys = { new NSString("Event_type") };
NSObject[] values = { new NSString("Recieve_Notification") };
var parameters = NSDictionary<NSString, NSObject>.FromObjectsAndKeys(keys, values, keys.Length);
// Send custom event
Firebase.Analytics.Analytics.LogEvent("CustomEvent", parameters);
if (application.ApplicationState == UIApplicationState.Active)
{
System.Diagnostics.Debug.WriteLine(userInfo);
var aps_d = userInfo["aps"] as NSDictionary;
var alert_d = aps_d["alert"] as NSDictionary;
var body = alert_d["body"] as NSString;
var title = alert_d["title"] as NSString;
//debugAlert(title, body);
}
}
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
// Do your magic to handle the notification data
System.Console.WriteLine(notification.Request.Content.UserInfo);
}
// Receive data message on iOS 10 devices.
public void ApplicationReceivedRemoteMessage(RemoteMessage remoteMessage)
{
Console.WriteLine(remoteMessage.AppData);
}
private void connectFCM()
{
//Messaging.SharedInstance.ShouldEstablishDirectChannel = true;
//Messaging.SharedInstance.Subscribe("/topics/topic");
Messaging.SharedInstance.Connect((error) =>
{
if (error == null)
{
Messaging.SharedInstance.Subscribe("/topics/topic");
}
System.Diagnostics.Debug.WriteLine(error != null ? "error occured" : "connect success");
});
}
These functions have been removed from many other posts on the same subject as happens when you get more and more desperate. I think the firebase console has been set up okay. I have added the iOS as an app along side the working Android one and have added an authentication key created on the Mac. (surprisingly I still get a token back even without this iOS app as part of the firebase project - not what I expected) A certificate has been created for this app and the provisioning profile has been recreated and old ones deleted.
I'm not sure what else to add - someone please put me out of my misery...

Got there in the end. Things for other people to check on - Moved App.Configure() into connectFCM
private void connectFCM()
{
Firebase.Core.App.Configure();
Messaging.SharedInstance.ShouldEstablishDirectChannel = true;
}
and added the following to info.plist
<key>NSExceptionDomains</key>
<dict>
<key>firebaseio.com</key>
<dict>
<key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>

Related

iOS Background notification C# Xamarin via Firebase

I am sending Firebase Notification on devices with Xamarin plugin.
This is my configuration : Visual Studio 2022. iOS 15. Xcode 13.
All is working fine in Android devices.
All is working fine in Apple devices except one thing : When the application is in background, the notification is received only when the user open the application.
Here is my code :
public class MVision : UIApplicationDelegate, IUNUserNotificationCenterDelegate, IMessagingDelegate
{
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions){
Firebase.Core.App.Configure();
// Messaging.SharedInstance.Self
// Messaging.messaging().delegate = self
Messaging.SharedInstance.Delegate = (IMessagingDelegate)Self;
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 = new MyUNUserNotificationCenterDelegate();
// 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;
UNUserNotificationCenter.Current.Delegate = new MyUNUserNotificationCenterDelegate();
UIApplication.SharedApplication.RegisterForRemoteNotifications();
The methods are :
[Export("messaging:didReceiveRegistrationToken:")]
public void DidReceiveRegistrationToken(Messaging messaging, string fcmToken)
{
var i = 1;
// var token = Messaging.SharedInstance.FcmToken;
// TODO: If necessary send token to application server.
// Note: This callback is fired at each app startup and whenever a new token is generated.
}
[Export("messaging:didRegisterForRemoteNotificationsWithDeviceToken:")]
public void DidRegisterForRemoteNotificationsWithDeviceToken(UIKit.UIApplication application, Foundation.NSData deviceToken) {
Messaging.SharedInstance.ApnsToken = deviceToken;
}
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo) { var userInfo1 = userInfo; }
[Export("application:didReceiveNotificationResponse:fetchCompletionHandler:")]
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, System.Action completionHandler)
{
completionHandler();
}
[Export("application:didReceiveRemoteNotification:fetchCompletionHandler:")]
public override void DidReceiveRemoteNotification(
UIApplication application,
NSDictionary userInfo,
System.Action<UIBackgroundFetchResult> completionHandler)
{
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// Except if "content-available" is set to true / 1
// Print full message.
// Console.WriteLine(userInfo);
completionHandler(UIBackgroundFetchResult.NewData);
}
[Export("messaging:didReceiveMessage:")]
public void DidReceiveMessage(Messaging messaging, RemoteMessage remoteMessage)
{
var notification = (NSDictionary)remoteMessage.AppData.ValueForKey(new NSString("notification"));
var title = notification.ValueForKey(new NSString("title"));
var text = notification.ValueForKey(new NSString("body"));
// remotenotification = true;
ScheduleNotification(title.ToString(), text.ToString());
}
//This code is for showing notification
void ScheduleNotification(string title, string body)
{
// Create content
var content = new UNMutableNotificationContent();
content.Title = title;
//content.Subtitle = "Subtitle";
content.Body = body;
content.Badge = 1;
content.CategoryIdentifier = "notification_fv";
content.Sound = UNNotificationSound.Default;
// Fire trigger in one seconds
var trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(1, false);
var requestID = "customNotification";
var request = UNNotificationRequest.FromIdentifier(requestID, content, trigger);
// This is the line that does the trick
UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) => {
if (err != null)
{
// Report error
System.Console.WriteLine("Error: {0}", err);
}
else
{
// Report Success
System.Console.WriteLine("Notification Scheduled: {0}", request);
}
});
}
/* [Export("messaging:didReceiveMessage:")]
public void DidReceiveMessage(Messaging messaging, RemoteMessage remoteMessage)
{
// Handle Data messages for iOS 10 and above.
HandleMessage(remoteMessage.AppData);
var fcmToken = Messaging.SharedInstance.FcmToken;
}*/
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, System.Action<UNNotificationPresentationOptions> completionHandler)
{
SystemSound.Vibrate.PlayAlertSound();
SystemSound.Vibrate.PlaySystemSound();
completionHandler(UNNotificationPresentationOptions.Alert);
}
The notification center delegate class is :
using Foundation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UIKit;
using UserNotifications;
namespace MVision.iOS
{
public class MyUNUserNotificationCenterDelegate : UNUserNotificationCenterDelegate
{
bool toggle;
public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
if (toggle)
completionHandler(UNNotificationPresentationOptions.Alert);
else
{
// Console.WriteLine(notification);
// completionHandler(UNNotificationPresentationOptions.None);
completionHandler(UNNotificationPresentationOptions.Alert);
}
toggle = !toggle;
}
}
}
I check the info.plist
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
<key>FirebaseScreenReportingEnabled</key>
<true/>
I also refer to entitlement.developer.plist :
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
I also parameter my notification with content-available = 1 and mutable-content = 1.
I think that the problem is that "DidReceiveRemoteNotification" is never call neither in background, nor in foreground.
Any Ideas ?
Please also notice : In Xamarin, when you stay in Visual Studio 2019 and you pass to iOS 15, you will not have simulators anymore, please download Visual Studio 2022.
Entitlement.plist does not work on this configuration. You must create Entitlement.developer.plist and refer to is.

xamarin push notification ios Page navigation not working when push notification click

I have azure push notification in Xamarin forms. when push notification received I need to open a page in my application. Its working fine in android. In iOS its not working when app is open. When app is in the background its working find in iOS.
This is my App Delegate code
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo,
Action<UIBackgroundFetchResult> completionHandler)
{
try
{
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
NSError error;
NSData notification = NSJsonSerialization.Serialize(userInfo["action"], NSJsonWritingOptions.PrettyPrinted, out error);
string notificationString = NSString.FromData(notification, NSStringEncoding.UTF8).ToString();
var NotificationObj = JsonConvert.DeserializeObject<NotificationData>(notificationString);
NotificationService.pushPageName = NotificationObj.Notification[0].PageName.ToString();
NotificationService.pushAppName = NotificationObj.AppName.ToString();
NotificationService.OpenPages = NotificationObj.Notification[0].OpenPage;
NotificationService.Notification = notificationString;
if (UIApplication.SharedApplication.ApplicationState.Equals(UIApplicationState.Active))
{
//App is in foreground. Act on it.
var application1 = new App(NotificationService.pushPageName, NotificationService.pushAppName, NotificationService.Notification);
LoadApplication(application1);
}
else
{
// var application1 = new App(NotificationService.pushPageName, NotificationService.pushAppName, NotificationService.Notification);
// LoadApplication(application1);
}
}
catch (Exception ex)
{
//LogInfo.ReportErrorInfo(ex.Message, ex.StackTrace, "AppDelegate-DidReceiveRemoteNotification");
}
}
after click push notification I need to open splash screen again.
This is App.cs Code
public App(string openPageName, string openAppName,string notification)
{
ServiceContainer.Resolve<IPushNotificationActionService>()
.ActionTriggered += NotificationActionTriggered;
InitMainPage(openPageName, openAppName, notification);
}
private void InitMainPage(string pageName,string appName,string notification)
{
ServiceContainer.Resolve<IPushNotificationActionService>()
.ActionTriggered += NotificationActionTriggered;
PushNotificationActionService.PushNotificationPageName = pageName ;
PushNotificationActionService.PushNotificationAppName = appName;
PushNotificationActionService.PushNotificationMessage = notification;
MainPage = new NavigationPage(new Splash(pageName));
}
All methods are calling and push notification data also loading correctly in iOS. But not navigating to Spalsh Screen. Anyone have an idea to resolve this please help.
If the app is running, we don't need to reload app with LoadApplication , we can directly send message to set MainPage in App.cs .
iOS
if (UIApplication.SharedApplication.ApplicationState.Equals(UIApplicationState.Active))
{
MessagingCenter.Send<object>(this,"Hi");
}
Forms
public App()
{
InitializeComponent();
MainPage = new MainPage();
MessagingCenter.Subscribe<object>(this, "Hi", (obj) => {
MainPage = new NavigationPage(new Splash(pageName));
});
}

Xamarin Firebase iOS not receiving FCM messages

I tried to implement Firebase push notifications and messages in my Xamarin Forms application. In Android it works fine, while in iOS I am receiving push notifications but I'm not receiving FCM data-only messages.
I am using Xamarin.Firebase.iOS.CloudMessaging package, and I followed this guide and this other one too.
This is my AppDelegate.cs code:
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IUNUserNotificationCenterDelegate, IMessagingDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Forms.SetFlags("Brush_Experimental");
Rg.Plugins.Popup.Popup.Init();
//Ascetic.Plugins.PopupDecorations.iOS.PrettyPopup.Init();
global::Xamarin.Forms.Forms.Init();
SfListViewRenderer.Init();
new Syncfusion.SfAutoComplete.XForms.iOS.SfAutoCompleteRenderer();
Syncfusion.XForms.iOS.TabView.SfTabViewRenderer.Init();
iOSMaterialFrameRenderer.Init();
LoadApplication(new App());
// Register your app for remote notifications.
configureFirebase();
var result = base.FinishedLaunching(app, options);
app.KeyWindow.TintColor = UIColor.White;
foreach (var fs in UIFont.FamilyNames.OrderBy(c => c).ToList())
{
Console.WriteLine(" * " + fs);
foreach (var f in UIFont.FontNamesForFamilyName(fs).OrderBy(c => c).ToList())
{
Console.WriteLine(" *-- " + f);
}
}
return result;
}
private void configureFirebase()
{
Firebase.Core.App.Configure();
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.Current.Delegate = this;
// iOS 10 or later
var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) =>
{
});
// 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);
}
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
//Firebase
[Export("messaging:didReceiveRegistrationToken:")]
public void DidReceiveRegistrationToken(Messaging messaging, string fcmToken)
{
Preferences.Set("tokenNotifica", fcmToken);
Console.WriteLine("\nValue: " + (fcmToken));
}
//APP IN BACKGROUND
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
handleNotification(userInfo);
completionHandler(UIBackgroundFetchResult.NewData);
}
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification,
Action<UNNotificationPresentationOptions> completionHandler)
{
handleNotification(notification.Request.Content.UserInfo);
completionHandler(UNNotificationPresentationOptions.Sound | UNNotificationPresentationOptions.Alert);
}
[Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action
completionHandler)
{
completionHandler();
NSDictionary userInfo = response.Notification.Request.Content.UserInfo;
try
{
var Data = userInfo[new NSString("gcm.notification.data")] as NSString;
if (Data != null && Data != "")
{
string[] tokens = Data.ToString().Split(',');
string Username = tokens[0];
string Password = tokens[1];
var Title = string.IsNullOrEmpty(response.Notification.Request.Content.Title) ? "No Notification available" : response.Notification.Request.Content.Title;
var Body = string.IsNullOrEmpty(response.Notification.Request.Content.Body) ? "No Message available" : response.Notification.Request.Content.Body;
}
}
catch (Exception)
{
}
}
[Export("messaging:didReceiveMessage:")]
public void DidReceiveMessage(Messaging messaging, RemoteMessage message)
{
handleNotification(message.AppData);
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
Messaging.SharedInstance.ApnsToken = deviceToken;
}
public void ApplicationReceivedRemoteMessage(RemoteMessage remoteMessage)
{
;
}
}
The DidReceiveRegistrationToken method is called with the token. If I send a notification to that token the method DidReceiveRemoteNotification is called, and the notification is successfully shown by the device. But if I send a data-only message to that token, nothing is called.
I tried disabling swizzling, enabling it, reconfigure Firebase from scratch and delete and recreate certificates and provisioning profiles in the Apple Dev website, but nothing worked. Also, I think all is configured correctly since my app is receiving notifications.
I hope I gave enough information, I am struggling with this for weeks
EDIT
I use this function in php to send the data-only message to devices
function sendMessage($deviceKey, $data){
$curl = curl_init();
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
"Content-Type: application/json",
"project_id: ***",
"Authorization: key=***"
));
// Set some options - we are passing in a useragent too here
curl_setopt_array($curl, [
CURLOPT_URL => 'https://fcm.googleapis.com/fcm/send',
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_USERAGENT => 'Codular Sample cURL Request',
CURLOPT_POST => 1,
]);
$body = [
'to' => $deviceKey,
"data" => $data
];
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($body));
$res = json_decode(curl_exec($curl), true);
curl_close($curl);
return $res;
}
This function is equal to the function that send the notification, but the $body array doesn't contain a "notification" key.
What I would like to do is use the data payload of the message in the application without display the notification, and this works just fine in Android, but not in iOS.

Unable to receive FCM push notification when iOS application is in background

Currently I have been working on a xamarin ios application, and I am trying to implement push notification using firebase cloud messaging, I have updated the info.plist, Entitlement.plist
and also added the GoogleService-Info.plist i.e.
and here is what I have done in my AppDelegate.cs
using Foundation;
using UIKit;
using UserNotifications;
using Firebase.Core;
using Firebase.CloudMessaging;
using System;
using TrendSurveyApp.iOS;
using Newtonsoft.Json;
using System.Collections.Generic;
using AudioToolbox;
namespace CloudMessagingSample
{
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to application events from iOS.
[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate, IUNUserNotificationCenterDelegate, IMessagingDelegate
{
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// Override point for customization after application launch.
// If not required for your application you can safely delete this method
//(Window.RootViewController as UINavigationController).PushViewController(new DialogViewController1(this), true);
App.Configure();
Messaging.SharedInstance.Delegate = this;
UIRemoteNotificationType notificationTypes = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
UNUserNotificationCenter.Current.Delegate = this;
UNUserNotificationCenter.Current.RequestAuthorization(UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound,
(granted, error) =>
{
if (granted)
{
InvokeOnMainThread(UIApplication.SharedApplication.RegisterForRemoteNotifications);
}
});
}
else if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var pushSettings = UIUserNotificationSettings.GetSettingsForTypes(
UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound,
new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(pushSettings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
else
{
UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(notificationTypes);
}
// To connect with FCM. FCM manages the connection, closing it
// when your app goes into the background and reopening it
// whenever the app is foregrounded.
Messaging.SharedInstance.ShouldEstablishDirectChannel = true;
return true;
}
public void DidReceiveRegistrationToken(Messaging messaging, string fcmToken)
{
// Monitor token generation: To be notified whenever the token is updated.
LogInformation(nameof(DidReceiveRegistrationToken), $"Firebase registration token: {fcmToken}");
// TODO: If necessary send token to application server.
// Note: This callback is fired at each app startup and whenever a new token is generated.
}
// You'll need this method if you set "FirebaseAppDelegateProxyEnabled": NO in GoogleService-Info.plist
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
Messaging.SharedInstance.ApnsToken = deviceToken;
}
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
Messaging.SharedInstance.AppDidReceiveMessage(userInfo);
LogInformation(nameof(DidReceiveRemoteNotification), userInfo);
completionHandler(UIBackgroundFetchResult.NewData);
var myData = JsonConvert.DeserializeObject<List<string>>(userInfo[new NSString("webContentList")] as NSString);
}
[Export("messaging:didReceiveMessage:")]
public void DidReceiveMessage(Messaging messaging, RemoteMessage remoteMessage)
{
// Handle Data messages for iOS 10 and above.
HandleMessage(remoteMessage.AppData);
var fcmToken = Messaging.SharedInstance.FcmToken;
LogInformation(nameof(DidReceiveMessage), remoteMessage.AppData);
}
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
SystemSound.Vibrate.PlayAlertSound();
SystemSound.Vibrate.PlaySystemSound();
completionHandler(UNNotificationPresentationOptions.Alert);
}
void HandleMessage(NSDictionary message)
{
if (message == null)
return;
MessageType messageType;
if (message.ContainsKey(new NSString("aps")))
messageType = MessageType.Notification;
else
messageType = MessageType.Data;
var e = new UserInfoEventArgs(message, messageType);
}
void LogInformation(string methodName, object information) => Console.WriteLine($"\nMethod name: {methodName}\nInformation: {information}");
}
}
Now the problem is when the app is in background i am not receiving any notification neither any callback function such as didReceivedNotification is being hit while if i have the app opened in the foreground i can receive notification !! Need Help
Try this:
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
LoadApplication(new App());
Firebase.Core.App.Configure();
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
// iOS => 10
var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) =>
{
//request authorization method
Console.WriteLine(granted);
});
// For iOS 10+ display notification (sent via APNS)
UNUserNotificationCenter.Current.Delegate = this;
}
else
{
// iOS 9 <=
var allNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound;
var settings = UIUserNotificationSettings.GetSettingsForTypes(allNotificationTypes, null);
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
}
UIApplication.SharedApplication.RegisterForRemoteNotifications();
return base.FinishedLaunching(application, launchOptions);
}

FCM Xamarin.Forms, iOS background Notification doesnt show

Platform: iOS 10.2+
Xamarin Plugin: Firebase iOS cloud messaging https://components.xamarin.com/view/firebaseioscloudmessaging
Problem: When I send a notification from FireBase console or from my code calling FireBase. My iPhones don't receive the background notifications(as a bubble). But, if I had the application in foreground, I received the notification as a "DisplayAlert" from the function ApplicationReceivedRemoteMessage(RemoteMessage remoteMessage).
So, the device is registered on FCM, the device have the token, the device can receive the notifications, but the device didnt receive the notifications on background.
** In VisualStudio 2017 at .iOS project manifest I have the Background mode activated and the remote notifications activated too **
¿It's this a common issue? ¿Can I solve for working at my project?
Code of AppDelegate
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IUNUserNotificationCenterDelegate, IMessagingDelegate
{
protected SQLiteAsyncConnection conn;
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
global::Xamarin.FormsMaps.Init();
CachedImageRenderer.Init();
LoadApplication(new App());
UITabBar.Appearance.SelectedImageTintColor = UIColor.FromRGB(139, 194, 77);
UINavigationBar.Appearance.TintColor = UIColor.FromRGB(139,194,77);
CrossVersionTracking.Current.Track();
// Firebase component initialize
Firebase.Analytics.App.Configure();
// 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;
// For iOS 10 data message (sent via FCM)
Messaging.SharedInstance.RemoteMessageDelegate = this;
}
else
{
// iOS 9 or before
var allNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound;
var settings = UIUserNotificationSettings.GetSettingsForTypes(allNotificationTypes, null);
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
}
UIApplication.SharedApplication.RegisterForRemoteNotifications();
Firebase.InstanceID.InstanceId.Notifications.ObserveTokenRefresh((sender, e) =>
{
newToken = Firebase.InstanceID.InstanceId.SharedInstance.Token;
//Conectamos con la base de datos.
database = new SQLiteClient();
conn = database.GetConnection();
usuario = null;
try
{
var task = Task.Run(async () =>
{
usuario = await conn.Table<Usuario>()
.FirstOrDefaultAsync();
});
task.Wait();
if (usuario != null)
{
usuario.token = newToken;
task = Task.Run(async () =>
{
await conn.InsertOrReplaceAsync(usuario);
});
task.Wait();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("TOKEN ERROR\tNo se ha podido Guardar el Token" + ex.Message);
}
System.Diagnostics.Debug.WriteLine("TOKEN\t" + newToken);
connectFCM();
});
#endregion
return base.FinishedLaunching(app, options);
}
public override void DidEnterBackground(UIApplication uiApplication)
{
Messaging.SharedInstance.Disconnect();
Console.WriteLine("Disconnected from FCM");
}
public override void OnActivated(UIApplication uiApplication)
{
connectFCM();
base.OnActivated(uiApplication);
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
#if DEBUG
Firebase.InstanceID.InstanceId.SharedInstance.SetApnsToken(deviceToken, Firebase.InstanceID.ApnsTokenType.Sandbox);
#endif
#if RELEASE
Firebase.InstanceID.InstanceId.SharedInstance.SetApnsToken(deviceToken, Firebase.InstanceID.ApnsTokenType.Prod);
#endif
}
// iOS 9 <=, fire when recieve notification foreground
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
Messaging.SharedInstance.AppDidReceiveMessage(userInfo);
// Generate custom event
NSString[] keys = { new NSString("Event_type") };
NSObject[] values = { new NSString("Recieve_Notification") };
var parameters = NSDictionary<NSString, NSObject>.FromObjectsAndKeys(keys, values, keys.Length);
// Send custom event
Firebase.Analytics.Analytics.LogEvent("CustomEvent", parameters);
if (application.ApplicationState == UIApplicationState.Active)
{
System.Diagnostics.Debug.WriteLine(userInfo);
var aps_d = userInfo["aps"] as NSDictionary;
var alert_d = aps_d["alert"] as NSDictionary;
var body = alert_d["body"] as NSString;
var title = alert_d["title"] as NSString;
debugAlert(title, body);
}
}
// iOS 10, fire when recieve notification foreground
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
System.Console.WriteLine(notification.Request.Content.UserInfo);
var title = notification.Request.Content.Title;
var body = notification.Request.Content.Body;
debugAlert(title, body);
}
private void connectFCM()
{
Console.WriteLine("connectFCM\tEjecutandose la función.");
Messaging.SharedInstance.Connect((error) =>
{
if (error == null)
{
//TODO: Change Topic to what is required
Messaging.SharedInstance.Subscribe("/topics/all");
}
//System.Diagnostics.Debug.WriteLine("connectFCM\t" + (error != null ? "error occured" : "connect success"));
Console.WriteLine("connectFCM\t" + (error != null ? "error occured" + error.DebugDescription : "connect success"));
});
}
private void debugAlert(string title, string message)
{
var alert = new UIAlertView(title ?? "Title", message ?? "Message", null, "Cancel", "OK");
alert.Show();
}
public void ApplicationReceivedRemoteMessage(RemoteMessage remoteMessage)
{
Console.WriteLine("\n*******************************************");
Console.WriteLine("AplicationReceivedRemoteMessage\t" + remoteMessage.AppData);
Console.WriteLine("\n*******************************************");
var title = remoteMessage.AppData.ValueForKey(new NSString("title"));
var text = remoteMessage.AppData.ValueForKey(new NSString("text"));
debugAlert("" + title, "" + text);
}
[Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
debugAlert("DidreceiveNotificationResponse", response + "" );
}
For more info, my info.plist contais the key:
<key>UIBackgroundModes</key>
<array>
<string>location</string>
<string>bluetooth-central</string>
<string>bluetooth-peripheral</string>
<string>fetch</string>
<string>remote-notification</string>
</array>
After a long investigation I solve it.
It is necesary to add that key to Entile.plist file.
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>

Resources