FCM Background Notification on IOS app in Xamarin C# not working - c#

The team is developing an IOS app on Xamarin in c# . Now we wanted to use the push notification service of fcm . Tried deploying the app but the issue is : The notifications are not received on ios if the app is in background. Did some research on it but found that the app disconnects from fcm when goes in background. Although tried not to disconnect it by not invoking the function but still the notifications were not received. Just wanted to know whether it's possible to receive the notification on ios while the app is in the background.
Sharing the relevant link and also the code for background that disconnects the app from fcm when it goes in background. Also removed the function call but it did not work.
public override void DidEnterBackground (UIApplication application)
{
// Use this method to release shared resources, save user data,
//invalidate timers and store the application state.
// If your application supports background exection this method is
//called instead of WillTerminate when the user quits.
Messaging.SharedInstance.Disconnect ();
Console.WriteLine (“Disconnected from FCM”);
}
Link:
https://components.xamarin.com/gettingstarted/firebaseioscloudmessaging/true

public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
App.Configure ();
// Register your app for remote notifications.
if (UIDevice.CurrentDevice.CheckSystemVersion (10, 0)) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.Current.Delegate = this;
var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
UNUserNotificationCenter.Current.RequestAuthorization (authOptions, (granted, error) => {
Console.WriteLine (granted);
});
} 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 ();
Messaging.SharedInstance.Delegate = this;
// 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;
}
Next put this in the AppDelegate.cs
[Export("messaging:didReceiveRegistrationToken:")]
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)
{
// Handle Notification messages in the background and foreground.
// Handle Data messages for iOS 9 and below.
// 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.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
//Messaging.SharedInstance.AppDidReceiveMessage (userInfo);
if(ConnectionClass.CompanyID != null)
{
SyncData.SyncDataDB();
}
//FirebasePushNotificationManager.CurrentNotificationPresentationOption = UNNotificationPresentationOptions.Alert;
FirebasePushNotificationManager.DidReceiveMessage(userInfo);
//HandleMessage(userInfo);
// Print full message.
//LogInformation(nameof(DidReceiveRemoteNotification), userInfo);
//completionHandler(UIBackgroundFetchResult.NewData);
}
[Export("messaging:didReceiveMessage:")]
public void DidReceiveMessage(Messaging messaging, RemoteMessage remoteMessage)
{
// Handle Data messages for iOS 10 and above.
HandleMessage(remoteMessage.AppData);
LogInformation(nameof(DidReceiveMessage), remoteMessage.AppData);
}
void HandleMessage(NSDictionary message)
{
if (MessageReceived == null)
return;
MessageType messageType;
if (message.ContainsKey(new NSString("aps")))
messageType = MessageType.Notification;
else
messageType = MessageType.Data;
var e = new UserInfoEventArgs(message, messageType);
MessageReceived(this, e);
}
public static void ShowMessage(string title, string message, UIViewController fromViewController, Action actionForOk = null)
{
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var alert = UIAlertController.Create(title, message, UIAlertControllerStyle.Alert);
alert.AddAction(UIAlertAction.Create("Ok", UIAlertActionStyle.Default, (obj) => actionForOk?.Invoke()));
fromViewController.PresentViewController(alert, true, null);
}
else
{
var alert = new UIAlertView(title, message, null, "Ok", null);
alert.Clicked += (sender, e) => actionForOk?.Invoke();
alert.Show();
}
}
void LogInformation(string methodName, object information) => Console.WriteLine($"\nMethod name: {methodName}\nInformation: {information}");

Related

subscriber not actively receiving published message

I have successfully connected MQTT to my broker both publisher and subscriber are working fine but there is a problem my subscriber is not actively receiving the message, the function mqtt_publishedRecieved triggers only one time,when I restart the subscriber app and only one message receives at a time. in order to get another message, I have to restart my app again. Well, as per my understanding it is because I called the config method of my class at startup. so therefore It checks for the subscribed topic only at startup. But I really want that functionality in my subscriber class to immediately receive the message once it is published by the receiver, which means both should work parallel.
My requirement is
=> if subscriber is connected, receives the message as soon as publisher fires.
=> if the subscriber is disconnected, message should be queued and later on all message will receive when subscriber connected again.
I do a research mqtt clean session set to false will ensure the persistent session so I set that flag to false to occupy persistent session, but it won't work for me.
it seems to me that I should add functionality to receive a message, for example, a button, so when the button is clicked it starts to get a message, but I can't set trigger/callback/button to receive the message for the subscribed topic. My app should start receiving all the published messages when service is started and stopped when it is disconnected.
below is the code of the publisher class.
public class publisher : IDatabaseSubscription
{
public publisher()
{
_mqttClient = new MqttClient("127.0.0.1");
_mqttClient.Connect("clientId", null, null, false, 60);
}
private void MQTT_OnChanged(object sender, RecordChangedEventArgs<EventDto> e)
{
if (_mqttClient != null && _mqttClient.IsConnected)
{
var message = System.Text.Encoding.UTF8.GetBytes("Hello from app 1");
var statusCode = _mqttClient.Publish("Message1",message , MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, true);
}
}
}
below is the code of subscriber class
public class subscriber
{
private MqttClient _mqttClient;
public subscriber()
{
}
public void configure ()
{
_mqttClient = new MqttClient("127.0.0.1");
_mqttClient.MqttMsgPublishReceived += client_MqttMsgPublishReceived;
var status = _mqttClient.Subscribe(new string[] { "Message1" }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
_mqttClient.Connect("clientId", null, null , false, 60) ;
}
private void client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
{
var message = System.Text.Encoding.UTF8.GetString(e.Message);
}
}
below is the code in startup
if(IsSubscriptionEnabled())
{
var service = _container.GetInstance<subscription>();
service.configure();
}
It was due to the same client id in both publisher and subscriber, broker kicked off the client if the new client connected with the same client id.

Pending Intent Extras are not set for Launcher Application

I have created a Launcher application named HomeActivity which has a LaunchMode = SingleTask and CATEGORY_HOME and CATEGORY_LAUNCHER. This activity in turn starts new activities and some other applications as well.
The activity is connected with a Firebase Messaging service to get Push notifications. The Firebase service adds some extras (gathered from the push notification and sends them in Extras in HomeActivity
public override void OnMessageReceived(RemoteMessage message)
{
Notification.Builder builder = new Notification.Builder(this);
builder.SetSmallIcon(Resource.Drawable.Icon);
Intent intent = new Intent(this, typeof(HomeActivity));
intent.AddFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
intent.PutExtra(General.UPDATE_NOTIFIED, true);
if(message.Data != null)
{
if (message.Data.Any())
{
foreach(KeyValuePair<string,string> kv in message.Data)
{
intent.PutExtra(kv.Key, kv.Value);
}
}
}
PendingIntent pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.UpdateCurrent);
builder.SetContentIntent(pendingIntent);
builder.SetLargeIcon(BitmapFactory.DecodeResource(Resources, Resource.Drawable.app_logo));
builder.SetContentTitle(message.GetNotification().Title);
builder.SetContentText(message.GetNotification().Body);
builder.SetDefaults(NotificationDefaults.Sound);
builder.SetAutoCancel(true);
NotificationManager notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Notify(1, builder.Build());
}
Whenever the HomeActivity is on top and a push notification is received and clicked, I can access the Extras in HomeActivity's OnResume function (I have overriden OnNewIntent(Intent intent)).
protected override void OnNewIntent(Intent intent)
{
bool updateNotification = intent.GetBooleanExtra(General.UPDATE_NOTIFIED, false); //Check if Extra is set
this.Intent = intent;
base.OnNewIntent(intent);
}
But when I am in another activity which is launched by the HomeActivity and a Push notification is clicked; the app does return to the HomeActivity but there are no Extras in the Intent.
I have tried all sorts of Flags, including but not limited to NewTask, ClearTask as well.
I am still unable to get why the Extras aren't being set when the notification is clicked at the time another activity is in place. What am I missing here.
Ok so I figured out what the issue is.
The Firebase system WILL NOT fire the OnMessageReceived method if you post a Notification with data (That is a display message).
What is needed to be done is that only data payload in the push notification will fire the OnMessageRecieved function which is responsible for setting the data.
So the notification should be like
{
to: "topic | Registered device",
data:{
data1: "data_value1",
data2: "data_value2",
data3: "data_value3",
}
}

Xamarin Form(PCL) IOS Firebase Push Message not working

I created a xamarin form project and I integrated firebase push notification in both Android & IOS projects. Its working fine on Android but not working with iOS.
I downloaded and added GoogleService-info.plist in iOS project, Set its Build Action to BundleResource.
AppDelegates.cs
namespace PushNotification.iOS
{
// 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 partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IUNUserNotificationCenterDelegate
{
//
// 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 App());
RegisterForNotificationFCM();
return base.FinishedLaunching(app, options);
}
private void RegisterForNotificationFCM()
{
//Firebase Cloud Messaging Configuration
//Get permission for notification
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
// iOS 10
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;
Messaging.SharedInstance.RemoteMessageDelegate = this as IMessagingDelegate;
}
else
{
// iOS 9 <=
var allNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound;
var settings = UIUserNotificationSettings.GetSettingsForTypes(allNotificationTypes, null);
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
}
UIApplication.SharedApplication.RegisterForRemoteNotifications();
Firebase.Analytics.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.Disconnect();
}
public override void OnActivated(UIApplication uiApplication)
{
connectFCM();
base.OnActivated(uiApplication);
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
Firebase.InstanceID.InstanceId.SharedInstance.SetApnsToken(deviceToken, Firebase.InstanceID.ApnsTokenType.Prod);
}
//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);
}
}
private void connectFCM()
{
Messaging.SharedInstance.Connect((error) =>
{
if (error == null)
{
Messaging.SharedInstance.Subscribe("/topics/topicName");
}
System.Diagnostics.Debug.WriteLine(error != null ? "error occured" : "connect success");
});
}
private void debugAlert(string title, string message)
{
var alert = new UIAlertView(title ?? "Title", message ?? "Message", null, "Cancel", "OK");
alert.Show();
}
}
}
Added all required Firebase libraries in IOS project & its building fine. But notification is not receiving on IOS simulator. Tell me what I am missing.
SOLVED
I had this same problem and was dealing with it for a day or two. The problem came down to the selected provisioning profile that was being used. When I changed my app id in the apple development portal to use push notification and downloaded my provisioning profile it created a second profile with the same name downloaded. This caused an error when it tried to select the correct one. Deleted the old provisioning profile and all is well now.
You cannot test push notification in the simulator
Please take a look on the Prerequisites.
For Cloud Messaging:
- A physical iOS device
- APNs certificate with Push Notifications enabled
- In Xcode, enable Push Notifications in App > Capabilities

WNS PushNotificationReceived does not intercept toast push notification

I'm writing a windows desktop app that relies on notifications to work. However, the event handler code, PushNotificationReceived on the channel does not seem to actually fire when I receive a notification. The following code is called to get the channel before its uri is sent to my server:
internal async Task<PushNotificationChannel> GetChannel()
{
PushNotificationChannel pnc;
try
{
pnc = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
if (_channel == null || !pnc.Uri.Equals(_channel.Uri))
{
_channel = pnc;
_channel.PushNotificationReceived += OnPushNotificationReceived;
Debug.WriteLine(_channel.Uri);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
_channel = null;
}
dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;
return _channel;
}
Such that anytime the channel is created or updated (via a different channel uri), it should assign the new channel's PushNotificationReceived event to the following (which is basically lifted from msdn's example):
void OnPushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs e)
{
string typeString = String.Empty;
string notificationContent = String.Empty;
switch (e.NotificationType)
{
//
//other notification types omitted for brevity
//
case PushNotificationType.Toast:
notificationContent = e.ToastNotification.Content.GetXml();
typeString = "Toast";
// Setting the cancel property prevents the notification from being delivered. It's especially important to do this for toasts:
// if your application is already on the screen, there's no need to display a toast from push notifications.
e.Cancel = true;
break;
}
Debug.WriteLine("Received notification, with payload: {0}", notificationContent);
string text = "Received a " + typeString + " notification, containing: " + notificationContent;
var ignored = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
MainPage.Current.ClearBanner();
});
}
Importantly, "MainPage.Current" is a reference to the app's main page as a static variable. The clear banner line simply removes a pink banner from the main page (just trying to get something simple working to start).
However, the code never seems to fire (no debug statement, pink banner remains). I am successfully getting the toast notification, and clicking on it will set focus to my app, so it's definitely not going to the wrong place.
Is there something I am doing wrong or some way to debug the notifications themselves?

Windows Phone 8 Raw Notification with Windows Azure Mobile Services

I'm trying to get a raw push notification to work from Azure Mobile Services to Windows Phone 8.
I've only signed up with Windows Azure for the free mobile services which comes with the free 20mb database and free mobile services.
The site to manage Windows Azure services has a link to an example of how to send a push notification to an app to update a flip tile which can be found here.
On insert into a table a script runs which sends the notification.
There's another example on MSDN which provides an example of how to create an ASP page that sends a raw notification to a WP8 app. That example is here.
I've gotten both examples to work but I need the first example to send a raw notification instead so the code in the second example works.
This is the code I have:
In my Windows Phone 8 app I have this to receive notifications, in App.xaml.cs:
private void AcquirePushChannel()
{
/// Holds the push channel that is created or found.
HttpNotificationChannel pushChannel;
// The name of our push channel.
string channelName = "RawSampleChannel";
// Try to find the push channel.
pushChannel = HttpNotificationChannel.Find(channelName);
if (pushChannel == null)
{
pushChannel = new HttpNotificationChannel(channelName);
// Register for all the events before attempting to open the channel.
pushChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(PushChannel_ChannelUriUpdated);
pushChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(PushChannel_ErrorOccurred);
pushChannel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(PushChannel_HttpNotificationReceived);
pushChannel.Open();
}
else
{
// The channel was already open, so just register for all the events.
pushChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(PushChannel_ChannelUriUpdated);
pushChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(PushChannel_ErrorOccurred);
pushChannel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(PushChannel_HttpNotificationReceived);
// Display the URI for testing purposes. Normally, the URI would be passed back to your web service at this point.
System.Diagnostics.Debug.WriteLine(pushChannel.ChannelUri.ToString());
//MessageBox.Show(String.Format("Channel Uri is {0}",
// pushChannel.ChannelUri.ToString()));
}
}
void PushChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
// Display the new URI for testing purposes. Normally, the URI would be passed back to your web service at this point.
System.Diagnostics.Debug.WriteLine(e.ChannelUri.ToString());
MessageBox.Show(String.Format("Channel Uri is {0}",
e.ChannelUri.ToString()));
});
}
void PushChannel_ErrorOccurred(object sender, NotificationChannelErrorEventArgs e)
{
// Error handling logic for your particular application would be here.
Deployment.Current.Dispatcher.BeginInvoke(() =>
MessageBox.Show(String.Format("A push notification {0} error occurred. {1} ({2}) {3}",
e.ErrorType, e.Message, e.ErrorCode, e.ErrorAdditionalData))
);
}
/// <summary>
/// Event handler for when a raw notification arrives. For this sample, the raw
/// data is simply displayed in a MessageBox.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void PushChannel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e)
{
string message;
using (System.IO.StreamReader reader = new System.IO.StreamReader(e.Notification.Body))
{
message = reader.ReadToEnd();
}
Deployment.Current.Dispatcher.BeginInvoke(() =>
MessageBox.Show(String.Format("Received Notification {0}:\n{1}",
DateTime.Now.ToShortTimeString(), message))
);
}
In Application Launching it calls AcquirePushChannel:
private void Application_Launching(object sender, LaunchingEventArgs e)
{
AcquirePushChannel();
}
My issue is in my Windows Azure Mobile Services database, where I have the following code on insert to a table to send the raw push notification, which doesn't work:
function insert(item, user, request) {
request.execute({
success: function () {
// Write to the response and then send the notification in the background
request.respond();
// for testing I'm manually putting in the channel ID where it says <channelID> below
push.mpns.sendRaw(<channelID>,
'test', {
success: function (pushResponse) {
console.log("Sent push:", pushResponse);
}
});
}
});
}
There is doc on this here, so I'm sure it's correct, but it just doesn't work.
And there's an example here.
One other question is, how can I view console.log via Windows Azure?
I was able to find out from the logs that my code wasn't sending the notification and worked out that it was my method of testing which was causing it and so I've fixed it. I've found out that the insert script only fires when I use the code:
private MobileServiceCollection<TodoItem, TodoItem> items;
private IMobileServiceTable<TodoItem> todoTable = App.MobileService.GetTable<TodoItem>();
private async void InsertTodoItem(TodoItem todoItem)
{
// This code inserts a new TodoItem into the database. When the operation completes
// and Mobile Services has assigned an Id, the item is added to the CollectionView
await todoTable.InsertAsync(todoItem);
items.Add(todoItem);
}
The insert script for example doesn't run if you use Management Studio and insert a row manually.

Categories