Changing action titles in interactive notifications at run time - ios

As per my understanding I have to register categories and actions therein for local or remote interactive notifications, now my requirement is that I want to show buttons with dynamic titles coming as part of the push payload.
As an alternative I have also tried the option of firing a local notification with settings registered when a remote notification is received, but somehow it is not working and the local notification is not fired. Help highly appreciated.

The short answer is that you can't change the action button title at runtime.
BUT, you can try the following:
Before sending a notification to the device, send it a "silent" notification, using the content-available property. In this notification, send a data which represent your new action button titles.
Update your relevant category using registerUserNotificationSettings:.
Send the "real" notification with the new action titles, you've just created.
It need to be tested, as I never tried it in a production mode. Also, take into consideration, that you're going to double up your notification sent amount.

For someone looking for this question's solution
1.Create a notification service extension
In didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler
create action buttons and register the category again to Notification Center
For ex:
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
NSDictionary * userInfo = request.content.userInfo;
NSArray * actionButtons = [userInfo valueForKeyPath:#"payload.actionButton"];
if(actionButtons){
NSMutableArray * buttons = [NSMutableArray new];
for(NSDictionary * actionButton in actionButtons){
UNNotificationAction *ActionBtn = [UNNotificationAction actionWithIdentifier:[actionButton valueForKey:#"actionDeeplink"] title:[actionButton valueForKey:#"actionName"] options:UNNotificationActionOptionNone];
[buttons addObject:ActionBtn];
}
UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:#"myNotificationCategory" actions:buttons intentIdentifiers:#[] options:UNNotificationCategoryOptionNone];
NSSet *categories = [NSSet setWithObject:category];
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:categories];
}
}
Then in your notification payload do something like this :
{
"aps" : {
"alert" : {
"title" : "Manish Malviya",
"body" : "iOS Developer"
},
"mutable-content" : 1,
"content-available" : 1,
"category" : "myNotificationCategory"
},
"payload" : {"actionButton": [
{
"actionName": "Yes",
"actionDeeplink": "nottest"
},
{
"actionName": "May be",
"actionDeeplink": "tes"
},
{
"actionName": "No",
"actionDeeplink": "test"
}
]
}
}
and voila see the output.

As #Asaf stated, need to re register with relevant category using registerUserNotificationSettings:
Just use the below code to achieve this :
let actionAnswer = UserNotificationAction(title: kAnswerEventTitle, identifier: kAnswerEventIdentitfier, activationMode: .Foreground, authenticationRequired: false, isDestructive: false)
let actionReject = UserNotificationAction(title: kRejectEventTitle, identifier: kRejectEventIdentitfier, activationMode: .Background, authenticationRequired: false, isDestructive: true)
registerModiifedUserNotificationCategory(userActions)
//For Notification Actions - object
struct UserNotificationAction {
var title: String
var identifier: String
var activationMode: UIUserNotificationActivationMode
var authenticationRequired: Bool
var isDestructive:Bool
init(title: String, identifier: String, activationMode: UIUserNotificationActivationMode? = .Background, authenticationRequired: Bool? = false, isDestructive: Bool? = false){
self.title = title
self.identifier = identifier
self.activationMode = activationMode!
self.authenticationRequired = authenticationRequired!
self.isDestructive = isDestructive!
}
}
//func for dynamic UIMutableUserNotificationCategory and UIMutableUserNotificationAction creation
func createCategoryUserNotification(notificationActions: [UserNotificationAction]) -> UIMutableUserNotificationCategory {
var UserNotificationActions = [UIMutableUserNotificationAction]()
for userAction in notificationActions {
let actionItem = UIMutableUserNotificationAction()
actionItem.activationMode = userAction.activationMode
actionItem.title = userAction.title
actionItem.identifier = userAction.identifier
actionItem.destructive = userAction.isDestructive
actionItem.authenticationRequired = userAction.authenticationRequired
UserNotificationActions.append(actionItem)
}
var runTimeCategoryUserNotification: UIMutableUserNotificationCategory{
let userCategory = UIMutableUserNotificationCategory()
userCategory.identifier = getGenericCategoryIdentifier(notificationActions)
userCategory.setActions(UserNotificationActions, forContext:.Default)
return userCategory
}
return runTimeCategoryUserNotification
}
//Re-Registering USer notification, if any new Categories required
func registerModiifedUserNotificationCategory( notificationActions: [UserNotificationAction])-> Bool {
// let UwerNotification = UserNotificationActions()
let newCategory = createCategoryUserNotification(notificationActions)
let settings = UIApplication.sharedApplication().currentUserNotificationSettings()
if settings!.types == .None {
return false
}
let oldUserNotificationsettings = UIApplication.sharedApplication().currentUserNotificationSettings()
var newCategories:[UIMutableUserNotificationCategory] = [UIMutableUserNotificationCategory]()
var isNewCategoryFoundInExist: Bool = false
for category in (oldUserNotificationsettings?.categories)! {
if category.identifier == newCategory.identifier{
isNewCategoryFoundInExist = true
}
if category.identifier == kGeneralCategoryUserNotification{//if any predefined Categories
newCategories.append(generalCategoryUserNotification)
}else if category.identifier == kCallCategoryUserNotification{//if any predefined Categories
newCategories.append(callCategoryUserNotififation)
}/* else{// some XYZ Category registered at runtime and if still want to Re-register
let actions = category.actionsForContext(.Default)
print(category.actionsForContext(.Default))
if actions?.count > 0 {
var genericCategoryUserNotififation: UIMutableUserNotificationCategory{
let userCategory = UIMutableUserNotificationCategory()
userCategory.identifier = UwerNotification.getGenericCategoryIdentifierFromUserNotifAction(actions!)
userCategory.setActions(actions, forContext:.Default)
return userCategory
}
newCategories.append(genericCategoryUserNotififation )
}
}*/
}
//REgister with new category of Notification if any
if !isNewCategoryFoundInExist {
newCategories.append(newCategory)
var categories = Set<UIUserNotificationCategory>()
for categr in newCategories{
categories.insert(categr)
}
let settings = UIUserNotificationSettings(forTypes:[.Alert, .Badge, .Sound], categories: categories)
//Register here registerUserNotificationSettings
}
return true
}
// generic Category Identifer based on concat of Action Identifiers
func getGenericCategoryIdentifier(notificationActions: [UserNotificationAction]) -> String {
let actionIdentifiers = notificationActions.map({$0.identifier})
let genericCategoryIdentifier = actionIdentifiers.joinWithSeparator("")
return genericCategoryIdentifier
}

Related

firestore cloud messaging iOS Swift

I cannot get a new record entry into my firestore document db to generate an alert to users.
IOS app fetches and updates firestore data with no issues
If I manually send a message from firebase my app gets the message no issues
I can deploy my cloud function to firebase with no errors
What am I doing wrong? Thanks for any help.
let functions = require('firebase-functions')
let admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)
let db = admin.firestore()
exports.announceAlert = functions.database
.ref('/alerts/{documentId}')
.onCreate((snapshot, context) => {
let alert = snapshot.val()
sendNotification(alert)
})
function sendNotification(alert) {
let title = alert.Incident_Number
let summary = alert.Flash_Summary
let status = alert.Incident_Status
let payload = {
notification: {
title: 'Critical Incident: ' + title,
body: 'Summary: ' + summary,
sound: 'default'
}
}
console.log(payload)
let topic = "Alerts"
admin.messaging().sendToTopic(topic, payload)
}
This is what I did and it worked. Please keep in mind that the user of your iOS app has to subscribe to the topic and you do that through the app. The code below is just a function telling firebase to send a notification to subscribed users when a new document is created in a certain repository.
let functions = require('firebase-functions')
let admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)
let db = admin.firestore()
exports.announceMessage = functions.firestore
.document('/myData/{documentId}')
.onCreate((snapshot, context) => {
let message = snapshot.data()
sendNotification(message)
})
function sendNotification(message) {
let title = message.column1
let notification = message.column2
let payload = {
notification: {
title: 'Some title: ' + title,
body: 'Some header: ' + notification
},
}
console.log(payload)
let topic = "yourTopic"
return admin.messaging().sendToTopic(topic, payload)
}

Xamarin iOS save on realm db when app is closed

I would like to save a notification on realm db when push notification is received while app is closed, it works when is running in foreground or background, but when it is closed not works and I cant catch any exception.
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
{
if (null != userInfo && userInfo.ContainsKey(new NSString("aps")))
{
NSDictionary aps = userInfo.ObjectForKey(new NSString("aps")) as NSDictionary;
string title = "Agendarum Informa";
string notificationMessage = string.Empty;
if (aps.ContainsKey(new NSString("title")))
title = (aps[new NSString("title")] as NSString).ToString();
if (aps.ContainsKey(new NSString("alert")))
notificationMessage = (aps[new NSString("alert")] as NSString).ToString();
UIAlertView alert = new UIAlertView()
{
Title = title,
Message = notificationMessage
};
try
{
var now = DateTime.Now;
Realm _realm;
_realm = Realm.GetInstance();
_realm.Write(() =>
{
_realm.Add(new Models.RealmObjects.Notification()
{
Title = alert.Title,
Message = notificationMessage,
Date = now
});
});
}
catch (Exception ex)
{
UIAlertView alertEx = new UIAlertView()
{
Title = "ERRO",
Message = ex.Message
};
alertEx.AddButton("OK");
alertEx.Show();
}
alert.AddButton("OK");
alert.Show();
}
}
NOTE: On Android it works fine...
Any ideias?
Thanks.

How to access parameters in Push Notification

I am using mvvmCross 5.3 and Xamarin 6.3 and need help to access data passed by notification.
When I get a notification, I'm getting a standard JSON that is sent by Apple. I also receive one more parameter that I'll use to point to some screen in my application.
In my AppDelegate, I have the following code:
public override bool FinishedLaunching(UIApplication application, NSDictionary options)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var setup = new Setup(this, Window);
setup.Initialize();
var startup = Mvx.Resolve<IMvxAppStart>();
startup.Start();
Window.MakeKeyAndVisible();
//Push Notifications
if (UIDevice.CurrentDevice.CheckSystemVersion(9, 0))
{
var pushSettings = UIUserNotificationSettings.GetSettingsForTypes(
UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound,
new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(pushSettings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
else
{
UIRemoteNotificationType notificationTypes = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(notificationTypes);
}
return ApplicationDelegate.SharedInstance.FinishedLaunching(application, options);
}
In the parameter options, I know that I get the information I need. I do not know how to access this information in my code.
2017-10-26 11:39:28.680 App.IOs[6733:2676232]
{
UIApplicationLaunchOptionsRemoteNotificationKey =
{
aps =
{
alert =
{
body = "Message";
title = "Title";
};
};
idOrder = 254;
};
}
You can create a method like this and call it in the iOS methods that receive the notifications in AppDelegate:
private void HandleReceivedNotification(NSDictionary userInfo = null)
{
if (userInfo != null)
{
var apsDictionary = userInfo["aps"] as NSDictionary;
var alertDictionary = apsDictionary["alert"] as NSDictionary;
var body = alertDictionary["body"].ToString();
var idOrder = userInfo["idOrder "].ToString();
}
}
But don't forget to include a try/catch and check if any variable is null.

FCM custom notification for ios

I know this question have been ask a lot of time in android but not ios. So I already test out push notification with FCM it work fine. Mine problem is wether it's a way to create custom notification rather than letting the system to handle the notification with the notification payload?
-Users can send data messages with FCM.What you have to send is,
Sample PostMan Json data:
{ "data": {
"any key": "any value",
"any key 2": "any value 2"
},
"to" : "token received to mobile from FCM"
}
Payload inside "data" can change according to the requirement.
Mobile end:
application(_:didReceiveRemoteNotification:) override method will get fire.So it is unto developer to extract data from the userInfo property in iOS and use it.
For ios using c# working code:-
public bool NotificationToiosDevice()
{
bool str;
try
{
//Create the web request with fire base API
WebRequest tRequest = WebRequest.Create("https://fcm.googleapis.com/fcm/send");
tRequest.Method = "post";
//serverKey - Key from Firebase cloud messaging server
tRequest.Headers.Add(string.Format("Authorization: key={0}", "AAAALNl-P7o:APA91bE38khU73UTdIj7L6LvVS3uI6f47REmxl.................................."));
//Sender Id - From firebase project setting
tRequest.Headers.Add(string.Format("Sender: id={0}", "12345"));
tRequest.ContentType = "application/json";
var payload = new
{
to = "dDDbFltwE5o:APA91bFmZZ-c1pNp................",
priority = "high",
content_available = true,
notification = new
{
title = "Title Of Notification",
},
data = new
{
body = new { Parameter1 = "FirstParameterValue", Parameter2 = "SecondParameterValue" },
},
};
string jsonNotificationFormat = Newtonsoft.Json.JsonConvert.SerializeObject(payload);
Byte[] byteArray = Encoding.UTF8.GetBytes(jsonNotificationFormat);
tRequest.ContentLength = byteArray.Length;
using (Stream dataStream = tRequest.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
using (WebResponse tResponse = tRequest.GetResponse())
{
using (Stream dataStreamResponse = tResponse.GetResponseStream())
{
if (dataStreamResponse != null) using (StreamReader tReader = new StreamReader(dataStreamResponse))
{
String sResponseFromServer = tReader.ReadToEnd();
// str = sResponseFromServer;
}
}
}
}
str = true;
}
catch (Exception ex)
{
str = false;
}
return str;
}

Push Notification in Xamarin Forms

I'm trying to implement Push Notification in PCL app in Xamarin forms IOS. I have a suitable provisioning profile and p12 file that I used to send notification to native app and I'm using it on my Xamarin forms, do I need to do something more beside this changes? I changed the AppDelegate in the IOS project to look like this:
using System;
using System.Collections.Generic;
using System.Linq;
using Foundation;
using UIKit;
namespace Punteam.iOS
{
[Register ("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
String model = UIDevice.CurrentDevice.Model;
String sysName = UIDevice.CurrentDevice.SystemName;
String sysVer = UIDevice.CurrentDevice.SystemVersion;
NSUserDefaults.StandardUserDefaults.SetString (model, "Model");
NSUserDefaults.StandardUserDefaults.SetString (sysName, "sysName");
NSUserDefaults.StandardUserDefaults.SetString (sysName, "sysVer");
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
{
UIRemoteNotificationType notificationTypes = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(notificationTypes);
}
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
//***********************************************************************************************
//** RegisteredForRemoteNotifications *
//***********************************************************************************************
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
// Get current device token
var DeviceToken = deviceToken.Description;
if (!string.IsNullOrWhiteSpace(DeviceToken)) {
DeviceToken = DeviceToken.Trim('<').Trim('>');
}
// Get previous device token
var oldDeviceToken = NSUserDefaults.StandardUserDefaults.StringForKey("PushDeviceToken");
// Has the token changed?
if (string.IsNullOrEmpty(oldDeviceToken) || !oldDeviceToken.Equals(DeviceToken))
{
//TODO: Put your own logic here to notify your server that the device token has changed/been created!
}
// Save new device token
NSUserDefaults.StandardUserDefaults.SetString(DeviceToken, "PushDeviceToken");
}
//***********************************************************************************************
//** ReceivedRemoteNotification *
//***********************************************************************************************
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
{
ProcessNotification(userInfo, false);
}
//***********************************************************************************************
//** ProcessNotification *
//***********************************************************************************************
void ProcessNotification(NSDictionary options, bool fromFinishedLaunching)
{
// Check to see if the dictionary has the aps key. This is the notification payload you would have sent
if (null != options && options.ContainsKey(new NSString("aps")))
{
//Get the aps dictionary
NSDictionary aps = options.ObjectForKey(new NSString("aps")) as NSDictionary;
string alertString = string.Empty;
string paramString = string.Empty;
if (aps.ContainsKey(new NSString("alert")))
alertString = (aps[new NSString("alert")] as NSString).ToString();
if (aps.ContainsKey(new NSString("param")))
paramString = (aps[new NSString("param")] as NSString).ToString();
if (!fromFinishedLaunching)
{
//Manually show an alert
if (!string.IsNullOrEmpty(alertString))
{
UIAlertView avAlert = new UIAlertView("Awesome Notification", alertString , null,
NSBundle.MainBundle.LocalizedString("Cancel", "Cancel"),
NSBundle.MainBundle.LocalizedString("OK", "OK"));
avAlert.Clicked += (sender, buttonArgs) =>
{
if (buttonArgs.ButtonIndex != avAlert.CancelButtonIndex)
{
if (!string.IsNullOrEmpty(paramString))
{
// App.Current.MainPage = new NavigationPage(new PushNotifMessageDisplay(paramString));
}
}
};
avAlert.Show();
}
}
}
}
//***********************************************************************************************
//** FailedToRegisterForRemoteNotifications *
//***********************************************************************************************
public override void FailedToRegisterForRemoteNotifications (UIApplication application , NSError error)
{
new UIAlertView("Error registering push notifications", error.LocalizedDescription, null, "OK", null).Show();
}
}
}
You can check my full example for push notification here
Regarding the iOS part, this is the relevant code that goes to your AppDelegate:
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
var deviceTokenString = deviceToken.ToString().Replace("<","").Replace(">", "").Replace(" ", "");
var notificationService = Resolver.Resolve<IPushNotificationService>();
var pushNotificationRegister = Resolver.Resolve<IPushNotificationRegister>();
if (pushNotificationRegister.ShouldSendToken(deviceTokenString))
{
var uid = UIDevice.CurrentDevice.IdentifierForVendor.AsString();
notificationService.AddPushToken(deviceTokenString, DeviceUtils.GetDeviceType(), uid);
}
}
*IPushNotificationRegister - checks if the token is already sent to the server (it is done to avoid unneeded requests to the server).
*IPushNotificationService - The service to send the token to the server.

Resources