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
Related
I added a functionality to my Xamarin Forms App allowing it to receive Notifications from Azure.
Everything works fine in debug mode, but in release mode, the App crashes when I receive a notification.
I got a logger working with Adb to see the exception. The code I am using is the following:
public void OnPushNotificationReceived(Context context, INotificationMessage message)
{
try
{
var msgData = message.Data;
if (msgData.ContainsKey("EventType"))
{
try
{
if (AllowStoreNotification(msgData["VirtualStoreId"], msgData["EventType"]))
{
SendNotification(message.Title, message.Body);
}
}
catch (KeyNotFoundException ex)
{
new TrackEvent("Could not deliver notification. Parameter error.")
.AddParameter("exception", ex.Message.ToString())
.Send();
}
}
}
catch (Exception ex)
{
Log.Error("Notification", ex.Message);
}
void SendNotification(string title, string body)
{
var notification = new NotificationCompat.Builder(context, "PushNotifications")
.SetContentTitle(title)
.SetContentText(body)
.SetSmallIcon(Resource.Drawable.logo_pink_circle)
.SetAutoCancel(true)
.SetDefaults((int)NotificationDefaults.All)
.SetPriority((int)NotificationPriority.High)
.Build();
if (_notificationManager is null)
{
NotificationManagerCompat.From(context).Notify(0, notification);
}
else
{
_notificationManager.Notify(0, notification);
}
}
}
The _notificationManager is null for version Build.VERSION.SdkInt >= BuildVersionCodes.O.
The exception from de ADB log adb logcat Notification:E *:S:
Notification: no non-static method "Lcom/microsoft/windowsazure/messaging/notificationhubs/BasicNotificationMessage;.getData()Ljava/util/Map;"
Anyone knows how to fix this?
I figured out how to make it work using the skip linking assembly.
The issue is the certificate you uploaded to azure, it's just for development, not for production
You have a few options:
Change the certificate once you move to production
2 clouds, one for development and another for production.
Or just compile your app as production every time you want to test notifications
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}");
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",
}
}
I am trying to showToast when the phone leaves or enter the geofenced location (which is set elsewhere and passed in). The issue is that when the app is in the background the trigger does not occur and I don't see the showToast message. I am changing the location manually using an emulator on my PC.
Background Tasks> Location is set under the app manifest.
This is the code I am using to build the Geofence and backgroundtask
//Creates Geofence and names it "PetsnikkerVacationFence"
public static async Task SetupGeofence(double lat, double lon)
{
await RegisterBackgroundTasks();
if (IsTaskRegistered())
{
BasicGeoposition position = new BasicGeoposition();
position.Latitude = lat;
position.Longitude = lon;
double radius = 8046.72; //5 miles in meters
Geocircle geocircle = new Geocircle(position, radius);
MonitoredGeofenceStates monitoredStates = MonitoredGeofenceStates.Entered | MonitoredGeofenceStates.Exited;
Geofence geofence = new Geofence("PetsnikkerVacationFence", geocircle, monitoredStates, false);
GeofenceMonitor monitor = GeofenceMonitor.Current;
var existingFence = monitor.Geofences.SingleOrDefault(f => f.Id == "PetsnikkerVacationFence");
if (existingFence != null)
monitor.Geofences.Remove(existingFence);
monitor.Geofences.Add(geofence);
}
}
//Registers the background task with a LocationTrigger
static async Task RegisterBackgroundTasks()
{
var access = await BackgroundExecutionManager.RequestAccessAsync();
if (access == BackgroundAccessStatus.Denied)
{
}
else
{
var taskBuilder = new BackgroundTaskBuilder();
taskBuilder.Name = "PetsnikkerVacationFence";
taskBuilder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));
taskBuilder.SetTrigger(new LocationTrigger(LocationTriggerType.Geofence));
taskBuilder.TaskEntryPoint = typeof(Petsnikker.Windows.Background.GeofenceTask).FullName;
var registration = taskBuilder.Register();
registration.Completed += (sender, e) =>
{
try
{
e.CheckResult();
}
catch (Exception error)
{
Debug.WriteLine(error);
}
};
}
}
static bool IsTaskRegistered()
{
var Registered = false;
var entry = BackgroundTaskRegistration.AllTasks.FirstOrDefault(keyval => keyval.Value.Name == "PetsnikkerVacationFence");
if (entry.Value != null)
Registered = true;
return Registered;
}
}
}
This code is where I monitor the state of the geofence.
This is where the Entry point in the appxmanifest is pointing
public sealed class GeofenceTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
var monitor = GeofenceMonitor.Current;
if (monitor.Geofences.Any())
{
var reports = monitor.ReadReports();
foreach (var report in reports)
{
switch (report.NewState)
{
case GeofenceState.Entered:
{
ShowToast("Approaching Home",":-)");
break;
}
case GeofenceState.Exited:
{
ShowToast("Leaving Home", ":-)");
break;
}
}
}
}
//deferral.Complete();
}
private static void ShowToast(string firstLine, string secondLine)
{
var toastXmlContent =
ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var txtNodes = toastXmlContent.GetElementsByTagName("text");
txtNodes[0].AppendChild(toastXmlContent.CreateTextNode(firstLine));
txtNodes[1].AppendChild(toastXmlContent.CreateTextNode(secondLine));
var toast = new ToastNotification(toastXmlContent);
var toastNotifier = ToastNotificationManager.CreateToastNotifier();
toastNotifier.Show(toast);
Debug.WriteLine("Toast: {0} {1}", firstLine, secondLine);
}
}
After looking at your code, it seems that your code is correct.
In order to fire the Geofence Backgroundtask to show the toast information, please make sure the following things:
1) Please make sure that you have done all the necessary configuration in the Package.appxmanifest for registering the BackgroundTask, for example you have set the correct EntryPoint and added the “Location” capabilities.
For the detailed information, you can try to compare your Package.appxmanifest with the official sample Geolocation’s Package.appxmanifest.
Please also check: Create and register a background task and Declare background tasks in the application manifest.
2) Please make sure that you know how to set the location in the Emulator manually for simulating the phone leave or enter the geofenced location. For more information about how to set location in the emulator, please check the following article:
https://msdn.microsoft.com/library/windows/apps/dn629629.aspx#location_and_driving .
3) Please make sure that your second position in your emulator is not really far away from the geofences that you have defined in the first time, because the emulator behaves like a real device, and the device doesn’t expect to suddenly move from New York to Seattle. Or the BackgroundTask will not be fire immediately.
4) Background tasks for geofencing cannot launch more frequently than every 2 minutes. If you test geofences in the background, the emulator is capable of automatically starting background tasks. But for the next subsequent background tasks, you need to wait for more than 2 minutes.
Besides, I will recommend you refer to the following article about how to use the Windows Phone Emulator for testing apps with geofencing:
https://blogs.windows.com/buildingapps/2014/05/28/using-the-windows-phone-emulator-for-testing-apps-with-geofencing/ .
Thanks.
I am planning to use Facebook login for the WEBGL version of my game.
Everytime i try to login i recieve "User cancelled login",
My application is public and Settings -> Advanced -> Client OAuth Settings - all are enabled
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Facebook.Unity;
public class FaceLogin : MonoBehaviour
{
void Awake()
{
if (!FB.IsInitialized)
{
// Initialize the Facebook SDK
FB.Init(InitCallback, OnHideUnity);
}
else
{
// Already initialized, signal an app activation App Event
FB.ActivateApp();
}
}
private void InitCallback()
{
if (FB.IsInitialized)
{
// Signal an app activation App Event
FB.ActivateApp();
// Continue with Facebook SDK
// ...
if (!FB.IsLoggedIn)
{
var perms = new List<string> {"public_profile", "email", "user_friends"};
FB.LogInWithReadPermissions(perms, AuthCallback);
}
}
else
{
Debug.Log("Failed to Initialize the Facebook SDK");
}
}
private void OnHideUnity(bool isGameShown)
{
Time.timeScale = !isGameShown ? 0 : 1;
}
private void AuthCallback(ILoginResult result)
{
if (FB.IsLoggedIn)
{
// AccessToken class will have session details
var aToken = AccessToken.CurrentAccessToken;
// Print current access token's User ID
Debug.Log(aToken.UserId);
// Print current access token's granted permissions
foreach (string perm in aToken.Permissions)
{
Debug.Log(perm);
}
}
else
{
Debug.Log("User cancelled login");
}
}
}
As I know, Facebook allow apps to use unity facebook sdk only for 'android', 'ios' and 'facebook canvas app'. I used facebook sdk for android and canvas. In android, you should use some hashes for debug and release mode for security.
And in canvas (it supports unity web player, webgl etc.), facebook allows logins only as facebook canvas application ( even in development, debug mode ). So if your app is not a facebook canvas app, you cannot login.