xamarin.ios receive notification when app is in background - push-notification

How can I handle notifications if my App in in Background and incative ?
Now, I get an Alert, which opens my App, when I tap on it.
But when the Notification receives I want to handle the ApplicationIconBadgeNumber.
Which method listen to notifications when my App is in Background?
HEres my AppDelegate.cs:
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
PushNotificationManager.DidRegisterRemoteNotifications(deviceToken);
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
PushNotificationManager.RemoteNotificationRegistrationFailed(error);
}
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
PushNotificationManager.DidReceiveMessage(userInfo);
}

Actually apple push notification are handled by iOS and not your app. We can't change the badge on receiving a push notification. DidReceiveRemoteNotification is triggred only if user tap on the notification. so updating badge number in this method is not wise.
We can alter the badge number when we are in the background state by sending the "badge" parameter in the push notification package.
Sample payload:
{
"aps" : {
"alert" : "message",
"badge" : 2
}
}
your app icon will show 2. Here the point is that calculation for badge count should be done in the server side.
Note that the badge parameter in the payload must be an integer.

Related

FirebasePushNotificationManager.Initialize freeze my app in iOS but no error appears

I am using Xamarin with C#.
FirebasePushNotificationManager.Initialize(options, true);
^ this code works fine in my Android Application.cs file, I can receive notifications in Android. However, when I add this code to AppDelegate.cs in iOS, it freezes my app, meaning there's no response when I click on button or input field. But if I remove this line of code, my iOS app works.
I've Googled a lot but didn't get any luck, can someone please help me?
Following is my AppDelegate.cs code:
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
ServicePointManager
.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
global::Xamarin.Forms.Forms.Init();
var iosClientHandler = new NSUrlSessionHandler();
Services.HttpClientService.HttpClientHandler = iosClientHandler;
LoadApplication(new App());
//This line is causing the issue
FirebasePushNotificationManager.Initialize(options, true);
return base.FinishedLaunching(app, options);
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
FirebasePushNotificationManager.DidRegisterRemoteNotifications(deviceToken);
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
FirebasePushNotificationManager.RemoteNotificationRegistrationFailed(error);
}
// To receive notifications in foreground on iOS 9 and below
// To receive notifications in background in any iOS version
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, 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.
// If you disable method swizzling, you'll need to call this method.
// This lets FCM track message delivery and analytics, which is performed
// automatically with method swizzling enabled
FirebasePushNotificationManager.DidReceiveMessage(userInfo);
// Do your magic to handle the notification data
System.Console.WriteLine(userInfo);
completionHandler(UIBackgroundFetchResult.NewData);
}
}

Flutter firebase receiving notification when closed or in background - how to pass message to class

I'm building a Flutter app with Firebase push notifications.
When a message is received I want the app to show a popup modal with the text.
When the app is in the foreground the popup modal displays - this works
When the app is the background and the message is received by the mobile it appears in the system tray, the user clicks on it, the app opens and the initial message is found and displayed to the user in the popup modal - eg. FirebaseMessaging.onMessageOpenedApp function - this works.
When the app is in the background, the notification is received by the phone (and the firebase listener is working because it outputs the message data using debugPrint to test), it appears in the system tray, but the user chooses NOT to click the message - when the app is brought back to the foreground the message is ignored - This is a problem.
The "FirebaseMessaging.onBackgroundMessage" function needs to be placed in the TOP LEVEL (outside of any class). Therefore when the app is once again placed in the foreground, how do I push message data from a message that may have been received whilst the app is in the background, in to my App Class to display the message content? I'm using "AppLifecycleState" to detect when the app is returned to the foreground, but I can't grab the message data because it is received in the top level, not in the class.
Please see my code below (see last few lines for the bit I'm stuck on)...
//TOP LEVEL-----
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
if (message.messageId!="") {
debugPrint("Have received a background message! Will have to grab the message from here somehow if the user didn't interact with the system tray message link");
}
}
Future<void> main() async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
//APP CLASS-----
class MyAppextends StatefulWidget {
State<MyApp> createState() => _MyAppState();
}
//APP STATE CLASS
class _MyAppState extends State<MyApp> with WidgetsBindingObserver{
#override
void initState() {
super.initState();
_initiateNotificationForegroundListener();
_initiateInteractedMessage();
}
// This variable will tell you whether the application is in foreground or not.
bool _isInForeground = true;
//Initiate Foreground Notification Listener (works)
void _initiateNotificationForegroundListener() {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
_handleNotificationInstruction(message);
});
}
//Initiate Background/Closed Notification Listener if user clicks the message in the system try (works)
Future<void> _initiateInteractedMessage() async {
RemoteMessage? message = await FirebaseMessaging.instance.getInitialMessage();
if (message != null) {
_handleNotificationInstruction(message);
}
// When app is in background (Stream listener)
FirebaseMessaging.onMessageOpenedApp
.listen(_handleNotificationInstruction);
}
void _handleNotificationInstruction(RemoteMessage message) {
//Create popup to display message info (works)
}
//Detect when an app moves in to the foreground
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
_isInForeground = state == AppLifecycleState.resumed;
if(_isInForeground){
/** HELP!!!
/* How can I check what message might have been received while app was in the background?? ie. the top-level _firebaseMessagingBackgroundHandler function??
**/
}
}
I was facing this problem too, this is annoying but thanks to this thread, I found the solution. The FCM's document states:
When received, an isolate is spawned (Android only, iOS/macOS does not
require a separate isolate) allowing you to handle messages even when
your application is not running.
An important note is that each isolate has its own memory so that your shared preferences in foreground will be different from it in background. You can "synchronize" the data by calling SharedPreference.reload() when your app resumes.
Save a message to a persistent storage and when the application starts check if the storage has pending messages.

android 12, how replace the notification trampolines

Having android sdk which intercept the push notification, and has been using notification trampoline to further open the end activity. In the case of deeplink the app who uses this sdk will open the configured deeplink handler activity.
Snippet for the trampoline:
public class NotificationTrampolineReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
final PendingResult asyncResult = goAsync();
ExecutorService executor = Executors.newSingleThreadExecutor();
asycTask(executor, new Runnable() {
#Override
public void run() {
String urlStr = getStringExtra(intent, PUSH_URL);
if (urlStr != null) {
var intent2: Intent = Intent(Intent.ACTION_VIEW, Uri.parse(urlStr));
if (intent2 != null) {
intent2.addFlags(FLAG_ACTIVITY_NEW_TASK);
intent2.addFlags(FLAG_ACTIVITY_BROUGHT_TO_FRONT);
context.startActivity(intent2);
logAnalytics(intent, Message.MessageAction.OPEN);
}
}
asyncResult.finish();
}
});
}
void asycTask(ExecutorService executor, final Runnable task) {
try {
executor.execute(task);
} catch (Throwable ex) {}
}
}
The notification trampolines is not working in Android 12 anymore.
The notification trampolines is needed in the sdk to intercept the click and do something like to log analytics event; closing the notification drawer when clicking at the Action button on the notification, etc. And this sdk does not know what activities the app may configure to handle the deeplinks.
Using a dummy activity to replace the trampoline would work, but not feel right, i.e. open the activity and inside to open another one then finish this one.
When android 12 puts restriction on the notification tramoline, does it suggest a replacement for the use case like the one here? Haven't find one.
What is the suggested new solution for intercepting the push notification tap first and then open the activity?
You are better off launching an Activity directly from the Notification. The Activity could then do the analytics and figure out what Activity needs to be launched and then delegate to that. As an alternative, the launched Activity could send the broadcast Intent to your BroadcastReceiver which could do the work.
NOTE: If you launch an Activity directly from a Notification, that is not a trampoline. The trampoline occurs when you launch a Service or a BroadcastReceiver directly from the Notification and that component then launches an Activity. The idea is that the user needs to be in control of what pops onto his screen. If he taps a notification, he expects that something will appear on his screen, if your Service launches an Activity, that can happen at any time and could possibly interrupt him.

How to pass a parameter from MainActivity to App.cs in method Resume

I have no idea how to pass a parameter from MainActivity.cs
this code from App.cs
protected override void OnResume(int notificationId)
{
MainPage = new AppShell(notificationId);
}
I plan to pass a number in the "notificationId" parameter. To find a row in the database by "notificationId".
If you're integrating push notification(e,g Azure notification hubs) into your application , OnResume is not the appropriate method to handle this .
Actually every push notification kit has its own trigger method when receiving notification (or tapping on the notification alert) .
For example , we can handle the notification in method OnPushNotificationReceived with Azure notification .
// Set the delegate for receiving messages
NotificationHub.SetListener(new SampleNotificationListener());
// The notification listener implementation
public class SampleNotificationListener : Java.Lang.Object, INotificationListener
{
public void OnPushNotificationReceived(Context context, INotificationMessage message)
{
Console.WriteLine($"Message received with title {message.Title} and body {message.Body}");
}
}
Refer to https://github.com/Azure/azure-notificationhubs-xamarin#getting-started-with-xamarinandroid.
Then we can use Messaging Center to send the data to Forms project if you want .
Something like
//Android
public void OnPushNotificationReceived(Context context, INotificationMessage message)
{
MessagingCenter.Send<object,int >(this, "Hi",notificationId);
}
//Forms App.cs
MessagingCenter.Subscribe<object,int> (this, "Hi", (sender,id) =>
{
MainPage = new AppShell(id);
});

Xamarin.Forms (iOS) MessagingCenter message from AppDelegate is not delivered

I have next goal: my app receives push notifications (FCM) and I need to handle user manipulations with them in all possible scenarios.
When I receive foreground push and handle it in WillPresentNotification - everything works as expected.
When I sent app to the background mode (clicking home button) and receive a push, click on that push at Notification Center I have next code in DidReceiveNotificationResponse method:
MessagingCenter.Send<object, PushNotificationObject>(this, "PushNavigationToRootPage", push);
So it just sends a message to the RootPage, which is subscribed/unsubscribed as below:
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Unsubscribe<object, PushNotificationObject>(this, "PushNavigationToRootPage");
MessagingCenter.Subscribe<object, PushNotificationObject>(this, "PushNavigationToRootPage", (sender, _push) => { ... });
}
protected override void OnDisappearing()
{
MessagingCenter.Unsubscribe<object, PushNotificationObject>(this, "PushNavigationToRootPage");
base.OnDisappearing();
}
enter code here
It also works! BUT!
If I completely close the app (swipe it off from App Switcher) and then click on the received push - it will only Send a message (from the code above), but will never reach code which is placed inside Subscribe of a RootPage.
Any ideas of what is wrong?
Thanks in advance!

Resources