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.
Related
I have developed app for ios and android in xamarin form cross platform. The problem I am facing in notification is that i can receive notification in android and its working absolutely fine, even I can open specific page from notification data, but in IOS I am not able to receieve notification. Here is my code from where I am sending notification
public async void NewTaskNotification(string user_token, string titlee, string bodyy, string openpage, long? mrno, DateTime? appointmentdate)
{
i++;
WebRequest tRequest = WebRequest.Create("https://fcm.googleapis.com/fcm/send");
tRequest.Method = "post";
tRequest.ContentType = "application/json";
var objNotification = new
{
to = user_token,
data = new
{
title = titlee,
body = bodyy,
priority = "high",
id = i,
icon = "ic_stat_ic_stat_ic_notification",
color = "#2F3C51",
Background = "#2F3C51",
open_page = openpage,
mr_no = mrno,
appointmentDate = appointmentdate,
},
};
string jsonNotificationFormat = Newtonsoft.Json.JsonConvert.SerializeObject(objNotification);
Byte[] byteArray = Encoding.UTF8.GetBytes(jsonNotificationFormat);
tRequest.Headers.Add(string.Format("Authorization: key={0}", "AAAAoQ445fk:APA91bHagr12v6bGUqE2d8soHCMXwo4rD6wyM_LFp6yD9b968J3SQQ9u8T5rsFBtsPzL-ct_cCjad_YPpEjaw5tq_OqR_asB5-zgKqyQfhV2djFxAAbK7PBPCZHMI2Y6KmNN8R-MItOA"));
tRequest.Headers.Add(string.Format("Sender: id={0}", "691728344569"));
tRequest.ContentLength = byteArray.Length;
tRequest.ContentType = "application/json";
using (Stream dataStream = tRequest.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
using (WebResponse tResponse = tRequest.GetResponse())
{
using (Stream dataStreamResponse = tResponse.GetResponseStream())
{
using (StreamReader tReader = new StreamReader(dataStreamResponse))
{
String responseFromFirebaseServer = tReader.ReadToEnd();
FCMResponse response = Newtonsoft.Json.JsonConvert.DeserializeObject<FCMResponse>(responseFromFirebaseServer);
if (response.success == 1)
{
Console.WriteLine("succeeded");
}
else if (response.failure == 1)
{
Console.WriteLine("failed");
}
}
}
}
}
}
with this code i am able to recieve notification and even open specific page but only with android, its not working with ios, but when i try to send notification to IOS from firebase console, its working fine.
Here is my appdelegate.cs
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication uiApplication, NSDictionary launchOptions)
{
Rg.Plugins.Popup.Popup.Init();
CachedImageRenderer.Init();
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
FirebasePushNotificationManager.Initialize(launchOptions, true);
FirebasePushNotificationManager.CurrentNotificationPresentationOption = UNNotificationPresentationOptions.Alert | UNNotificationPresentationOptions.Badge;
UNUserNotificationCenter.Current.Delegate = new UserNotificationCenterDelegate();
return base.FinishedLaunching(uiApplication, launchOptions);
}
public class UserNotificationCenterDelegate : UNUserNotificationCenterDelegate
{
public UserNotificationCenterDelegate()
{
}
public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
// Do something with the notification
Console.WriteLine("Active Notification: {0}", notification);
// Tell system to display the notification anyway or use
// `None` to say we have handled the display locally.
completionHandler(UNNotificationPresentationOptions.Alert);
}
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
FirebasePushNotificationManager.DidRegisterRemoteNotifications(deviceToken);
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
FirebasePushNotificationManager.RemoteNotificationRegistrationFailed(error);
}
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
FirebasePushNotificationManager.DidReceiveMessage(userInfo);
// Do your magic to handle the notification data
System.Console.WriteLine(userInfo);
completionHandler(UIBackgroundFetchResult.NewData);
}
}
I need to know where I am doing wrong for IOS
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.
I am trying to add push notifications to my Xamarin.iOS app using this tutorial
https://learn.microsoft.com/en-us/azure/notification-hubs/xamarin-notification-hubs-ios-push-notification-apns-get-started
I have followed all the steps as it is and below is what I have tried so far :-
AppDelegate.cs
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
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);
}
// If you have defined a root view controller, set it here:
UIStoryboard storyboard = UIStoryboard.FromName("Main", null);
var myViewController = storyboard.InstantiateViewController("Login_VC") as LoginViewController;
Window.RootViewController = myViewController;
// make the window visible
Window.MakeKeyAndVisible();
return true;
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
Hub = new SBNotificationHub(ServiceConstants.ListenConnectionString, ServiceConstants.NotificationHubName);
string token = deviceToken.ToString().Replace("<", "").Replace(">", "").Replace(" ", "");
deviceToken = NSData.FromString(token);
Hub.UnregisterAllAsync(deviceToken, (error) =>
{
if (error != null)
{
Console.WriteLine("Error calling Unregister: {0}", error.ToString());
return;
}
NSSet tags = null; // create tags if you want
Hub.RegisterNativeAsync(deviceToken, tags, (errorCallback) =>
{
if (errorCallback != null)
Console.WriteLine("RegisterNativeAsync error: " + errorCallback.ToString());
});
});
}
I have signed my app with a development provisioning profile from the apple developer console which is signed with an AppId with Push notifications enabled on it.
But when I get an error at the very beginning while registering for push notifications as follows :-
Error Response:<Error><Code>401</Code><Detail>ExpiredToken: .TrackingId:94e8c47b-1221-4188-8a75-9b744e12c9d7_G4,TimeStamp:1/20/2017 10:05:51 AM</Detail></Error>
Fail to perform registration operation.
<NSMutableURLRequest: 0x170018a70> { URL: https://mynotificationnamespace.servicebus.windows.net/mynotificationhub/Registrations/?$filter=deviceToken+eq+'62383062663335386437646233313263613766656165323535626634646537393438623262616238393164393466393766353030666166313966623339323232'&api-version=2013-04 }
Headers:{
Authorization = "SharedAccessSignature sr=http%3a%2f%2fmynotificationnamespace.servicebus.windows.net%2fmynotificationhub%2fregistrations%2f%3f%24filter%3ddevicetoken%2beq%2b%2762383062663335386437646233313263613766656165323535626634646537393438623262616238393164393466393766353030666166313966623339323232%27%26api-version%3d2013-04&sig=dpGnfYj19D%2FseR3GlTrE6Ay0ASdc%2BwFwWRNf3sxBE0%3D&se=148394919&skn=DefaultListenSharedAccessSignature";
"Content-Type" = "application/xml";
"User-Agent" = "NOTIFICATIONHUBS/2013-04(api-origin=IosSdk; os=iOS; os_version=10.2;)";
}
I am able to get the Push notifications working from the same Notification hub for Xamarin.Android but not Xamarin.iOS.
Any help would be appreciated.
Answering my own question as it might be useful for people looking for something similar. Turns out the Automatic DateTime was disabled on my device hence the device token was fetching as invalid or expired as the time on the device was lagging behind with 30 mins.
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.
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
}