Push notifications on Xamarin IOS - c#

we're developing a cross platform(ios and android) app on Xamarin using Xamarin.Forms. We've managed to get the same application working on IOS and Android. Great!
We would like to include push notifications in our app and this already works in Android. For IOS however, it's a completely different story.. No bare bones library to be found, Anywhere!!
For android we used the Google Cloud Messaging Client by Redth, this libary is so easy to use. We had it running in 2 hours or less. However, Nothing to be found anything like this for IOS.
How do i register my IOS device for push notifications in xamarin? We already have the right certificates, etc. its only the device side we need to get working. Something to point me in the right direction?

Use this push notification link for getting or enabling push notification services in IOS.
You will need to do following steps:
1> Create and download SSL and APNS certificates and push notification enabled provisioning profile.
2> First double click on SSL certificates than APNS certificates than provisioning profile.
3.> Now export p12 file from key-chain access and create PEM file from command prompt.
4> Now Register for push notification within FinishedLaunching.
5.> Run your program , You will get a device token and send it to the server.
Now APNS server would send this notification respective to the token.

You might want to look into PushSharp to power the server side part of the iOS and Android notifications. I believe they also have a Xamarin.iOS library you can use to subscribe to the push notifications.

// Register for push notifications.
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
var authOptions = UserNotifications.UNAuthorizationOptions.Alert | UserNotifications.UNAuthorizationOptions.Badge | UserNotifications.UNAuthorizationOptions.Sound;
UserNotifications.UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) =>
{
Console.WriteLine(granted);
});
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
else if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var settings = UIUserNotificationSettings.GetSettingsForTypes(UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound, new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
else
{
var notificationTypes = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(notificationTypes);
}
}
private SBNotificationHub Hub { get; set; }
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
Hub = new SBNotificationHub(Constants.ListenConnectionString, Constants.NotificationHubName);
//if user is not logged In
Employee employee = JsonConvert.DeserializeObject<Employee>(Settings.CurrentUser);
if (employee != null)
{
NSSet tags = new NSSet(new string[] { "username:" + employee.Email }); // create tags if you want
Hub.RegisterNativeAsync(deviceToken, tags, (errorCallback) =>
{
if (errorCallback != null)
Console.WriteLine("RegisterNativeAsync error: " + errorCallback.ToString());
});
}
else
{
Hub.UnregisterAllAsync(deviceToken, (error) =>
{
if (error != null)
{
Console.WriteLine("Error calling Unregister: {0}", error.ToString());
return;
}
});
}
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
AzurePushNotificationManager.RemoteNotificationRegistrationFailed(error);
}
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
{
ProcessNotification(userInfo, false);
}
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 alert = string.Empty;
if (aps.ContainsKey(new NSString("alert")))
alert = (aps[new NSString("alert")] as NSString).ToString();
if (!fromFinishedLaunching)
{
//Manually show an alert
if (!string.IsNullOrEmpty(alert))
{
NSString alertKey = new NSString("alert");
UILocalNotification notification = new UILocalNotification();
notification.FireDate = NSDate.Now;
notification.AlertBody = aps.ObjectForKey(alertKey) as NSString;
notification.TimeZone = NSTimeZone.DefaultTimeZone;
notification.SoundName = UILocalNotification.DefaultSoundName;
UIApplication.SharedApplication.ScheduleLocalNotification(notification);
}
}
}
}

Related

Xamarin.Forms - iOS RegisteredForRemoteNotifications not getting called

I am trying to register a physical iOS (13) device for remote notifications using Xamarin.iOS in a Xamarin.Forms project.
After it worked perfectly fine for about two weeks, I face the issue that after I call
UIApplication.SharedApplication.RegisterForRemoteNotifications()
(See complete code below)
Neither RegisteredForRemoteNotifications nor FailedToRegisterForRemoteNotifications of the AppDelegate is called.
The only thing that changed is that I added a production APNS-Certificate to my app-id that now looks like this:
App id redacted with xxxx
I can select the associated development-provisioning profile in Xcode and I can deploy the app without errors.
The relevant code from the AppDelgate:
FinishedLaunching:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Forms.SetFlags("IndicatorView_Experimental", "SwipeView_Experimental");
Forms.Init();
//other init methods omitted
LoadApplication(new App());
var baseFinished = base.FinishedLaunching(app, options);
RegisterForRemoteNotifications();
return baseFinished;
}
RegisterForRemoteNotifications:
public void RegisterForRemoteNotifications()
{
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
var options = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Sound |
UNAuthorizationOptions.Sound;
UNUserNotificationCenter.Current.RequestAuthorization(options,(granted, error) =>
{
if (granted)
{
//this method is getting called
InvokeOnMainThread(UIApplication.SharedApplication.RegisterForRemoteNotifications);
}
});
}
//checking for other iOS versions omitted
}
RegisteredForRemoteNotifications and FailedToRegisterForRemoteNotifications - Both are not getting called:
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
var bytes = deviceToken.ToArray();
var token = BitConverter.ToString(bytes).Replace("-", "");
Debug.WriteLine($"Got iOS notification token: {token}");
DependencyService.Get<INotificationRegistrationService>().OnTokenAcquired(token, DeviceType.Apple);
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
Debug.WriteLine("error on register remote notifications ");
}
I may have just made a mistake in the code or with the certificates.
For anyone with the same problem, try using a mobile hotspot / mobile internet to register the iPhone. This is what solved it for me
I guess my firewall somehow blocked communication with the apns server.
I have no clue why because it worked before.
Now I will investigate how exactly the communication got blocked and if I have any relevant information, I will post them here.

Xamarin.iOS Firebase not receiving push notifications but receiving token

I'm setting up push notifications in a Xamarin.Forms project. I already did everything for Xamarin.Forms.Android and it works without any problem but I'm getting a lot of troubles with the iOS part. This isn't even the first time I do this but I still can't figure out what's going on.
What I did:
1. Included Nuget Xamarin.Firebase.iOS.CloudMessaging v3.1.2 (which is not the latest, but the latest is not even building because of library errors)
2. Create Firebase application and follow the usual setup, uploaded my .p12 for push notifications and added my Team ID.
3. Added my GoogleService-Info.plist and set it's BuildAction to "BundleResource"
4. Updated my Info.plist
5. Made sure that my App ID in Apple Developer Program included Push Notifications and the correct certificate
6. Added every piece of code I could related to the UserNotifications.IUNUserNotificationCenterDelegate, IMessagingDelegate interfaces
7. Retrieved my token from
Firebase.InstanceID.InstanceId.Notifications.ObserveTokenRefresh((sender, e) =>
{
var token = Messaging.SharedInstance.FcmToken;
if (!string.IsNullOrEmpty(token))
{
//AppData.Instance.TokenMobile = newToken;
}
});
Went back to Firebase console, created a test push and tried to send it both via "Invia messagio di prova" ("Send message") which made me specify a single token and via publishing.
My AppDelegate.cs
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, UserNotifications.IUNUserNotificationCenterDelegate, IMessagingDelegate
{
//
// 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();
global::Xamarin.Forms.FormsMaterial.Init();
LoadApplication(new App());
Firebase.Core.App.Configure();
// For iOS 10 data message (sent via FCM)
Messaging.SharedInstance.Delegate = this;
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.Current.Delegate = this;
// iOS 10 or later
var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) =>
{
if (granted)
{
InvokeOnMainThread(() =>
{
UIApplication.SharedApplication.RegisterForRemoteNotifications();
});
}
});
}
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();
// Handle token as you wish
Firebase.InstanceID.InstanceId.Notifications.ObserveTokenRefresh((sender, e) =>
{
var token = Messaging.SharedInstance.FcmToken;
if (!string.IsNullOrEmpty(token))
{
//AppData.Instance.TokenMobile = newToken;
}
});
UIApplication.SharedApplication.StatusBarHidden = true;
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
return base.FinishedLaunching(app, options);
}
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
{
}
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
}
[Export("messaging:didRefreshRegistrationToken:")]
public void DidReceiveRegistrationToken(Messaging messaging, string fcmToken)
{
Console.WriteLine($"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.
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
//Tested
//Messaging.SharedInstance.ApnsToken = deviceToken;
}
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
var userInfo = notification.Request.Content.UserInfo;
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.SharedInstance.AppDidReceiveMessage (userInfo);
// Print full message.
Console.WriteLine(userInfo);
// Change this to your preferred presentation option
completionHandler(UNNotificationPresentationOptions.None);
}
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action boh)
{
}
[Export("messaging:didReceiveMessage:")]
public void DidReceiveMessage(Messaging messaging, RemoteMessage message)
{
}
}
I also added this key to the Entitlment.plist file:
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
At this point I was expecting to receive something in my app, but I can't receive anything.
I put some breakpoints in each callback I tried to implement but none of them is getting called.
If it can be helpful, not even "DidReceiveRegistrationToken" method is getting called.
Solved the problem.
I don't know exactly why but I found in my GoogleService-Info.plist a different API Key from the one specified in the Firebase Console. I updated the key and I'm now receiving notifications.

Unity/Firebase How to authenticate using Google?

I'm trying to implement Firebase Authentication system in my Unity Game Project. Everything is setup properly on the console panel on the website. I've read the docs and can't find a way to login into Google using any api within Firebase within Unity. So I bought Prime31's Play GameServices Plugin for Unity.
Here are my questions:
How to authenticate using Google right within Firebase? Do I need to manage the google sign in myself?
In the Firebase docs I did find:
"After a user successfully signs in, exchange the access token for a Firebase credential, and authenticate with Firebase using the Firebase credential:"
Firebase.Auth.Credential credential = Firebase.Auth.GoogleAuthProvider.GetCredential(googleIdToken, googleAccessToken);
auth.SignInWithCredentialAsync(credential).ContinueWith(task => {
//......//
});
How can I get the googleIdToken, googleAccessToken which are being passed as parameters above?
Please help (with code). I really like Firebase and would like to make it work without any third party plugins like PRIME31.
Here is the entirety of my Google SignIn code w/ Firebase Authentication and GoogleSignIn libraries:
private void SignInWithGoogle(bool linkWithCurrentAnonUser)
{
GoogleSignIn.Configuration = new GoogleSignInConfiguration
{
RequestIdToken = true,
// Copy this value from the google-service.json file.
// oauth_client with type == 3
WebClientId = "[YOUR API CLIENT ID HERE].apps.googleusercontent.com"
};
Task<GoogleSignInUser> signIn = GoogleSignIn.DefaultInstance.SignIn();
TaskCompletionSource<FirebaseUser> signInCompleted = new TaskCompletionSource<FirebaseUser>();
signIn.ContinueWith(task =>
{
if (task.IsCanceled)
{
signInCompleted.SetCanceled();
}
else if (task.IsFaulted)
{
signInCompleted.SetException(task.Exception);
}
else
{
Credential credential = Firebase.Auth.GoogleAuthProvider.GetCredential(((Task<GoogleSignInUser>)task).Result.IdToken, null);
if (linkWithCurrentAnonUser)
{
mAuth.CurrentUser.LinkWithCredentialAsync(credential).ContinueWith(HandleLoginResult);
}
else
{
SignInWithCredential(credential);
}
}
});
}
The parameter is for signing in with intentions of linking the new google account with an anonymous user that is currently logged on. You can ignore those parts of the method if desired. Also note all of this is called after proper initialization of the Firebase Auth libraries.
I used the following libraries for GoogleSignIn: https://github.com/googlesamples/google-signin-unity
The readme page from that link will take you through step-by-step instructions for getting this setup for your environment. After following those and using the code above, I have this working on both android and iOS.
Here is the SignInWithCredential method used in the code above:
private void SignInWithCredential(Credential credential)
{
if (mAuth != null)
{
mAuth.SignInWithCredentialAsync(credential).ContinueWith(HandleLoginResult);
}
}
mAuth is a reference to FirebaseAuth:
mAuth = Firebase.Auth.FirebaseAuth.DefaultInstance;
The simple answer is that there's no way in the Firebase SDK plugin for Unity to do the entirety of Google's authentication in a Unity app. I would recommend following the instructions for email/password sign on to start out.
https://firebase.google.com/docs/auth/unity/start
If you really want Google sign on (which you probably do for a shipping title), this sample should walk you through it: https://github.com/googlesamples/google-signin-unity
The key is to get the id token from Google (which is a separate step from the Firebase plugin), then pass that in.
I hope that helps (even if it wasn't timely)!
For someone asking for HandleLoginResult from #Mathew Coats, here is the function used to handle after.
private void HandleLoginResult(Task<FirebaseUser> task)
{
if (task.IsCanceled)
{
UnityEngine.Debug.LogError("SignInWithCredentialAsync was canceled.");
return;
}
if (task.IsFaulted)
{
UnityEngine.Debug.LogError("SignInWithCredentialAsync encountered an error: " + task.Exception.InnerException.Message);
return;
}
else
{
FirebaseUser newUser = task.Result;
UnityEngine.Debug.Log($"User signed in successfully: {newUser.DisplayName} ({newUser.UserId})");
}
}
As first, you need use Google Sign in Unity plugin to login with Google, and then, when you logged, get token and continues with Firebase Auth. Also you can try this asset http://u3d.as/JR6
Here is the code to get access token from firebase after authentication is done
FirebaseUser mUser = FirebaseAuth.getInstance().getCurrentUser();
mUser.getToken(true)
.addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
#Override
public void onComplete(#NonNull Task<GetTokenResult> task) {
if (dialog != null) {
dialog.dismiss();
}
if (task.isSuccessful()) {
String idToken = task.getResult().getToken();
Log.i(getClass().getName(), "got access token :" + idToken);
} else {
// show logs
}
}
});

Push sharp - APNS Notifications not being sent

I'm currently developing an app with Telerik, and I need push notifications, I have set this up using Push Sharp in my api, gcm notifications are sending correctly to android phones, but when I run sendToiOS, iPhones do not receive APNS notifications.
I have checked using break points and can see that OnNotificationSucceeded is being called, and OnNotificationFailed is not. Which would be a good sign, however the Notifications don't seem to be coming through, I think I may have something wrong with the configuration of the APNSServiceBroker. Please note that "AppleCert" is a byte array containing the .p12 certificate for the app.
private static void sendToiOS(string deviceRegId, object notificationToSend, FERIS.Data.Model.Notification notification)
{
string json = JsonConvert.SerializeObject(notificationToSend);
Apns.QueueNotification(new ApnsNotification
{
DeviceToken = deviceRegId,
Payload = JObject.Parse(json)
});
}
var config = new ApnsConfiguration(ApnsConfiguration.ApnsServerEnvironment.Production,AppleCert,"IFB_4222");
Apns = new ApnsServiceBroker(config);
Apns.OnNotificationFailed += (NotificationFailed, aggregateEx) => {
aggregateEx.Handle(ex => {
return true;
});
};
Apns.OnNotificationSucceeded += (NotificationSent) => {
Console.WriteLine("Notification Sent!");
sentTotal++;
};

Send email from Xamarin.Forms app using Dependency Service

In my app, for debugging purposes and testing purposes I need to send an email.
How do I present the mail controller, when the class that sends it does not contain a definition for PresentViewController ? The class needs to fire off the email app after the user clicks "yes" from the alert which fires.
public async Task<bool> SendEmail(Exception ex)
{
var result = await SendNotificationToRequestSendingEmail();
if (result)
{
if (MFMailComposeViewController.CanSendMail)
{
mailController = new MFMailComposeViewController();
mailController.SetToRecipients(new string[] { "test#one.com", "test#two.com" });
mailController.SetSubject("Details");
mailController.SetMessageBody($"Message: {ex.Message}" +
$"Exception: {ex.ToString()}"
, false);
mailController.Finished += (object s, MFComposeResultEventArgs args) =>
{
Console.WriteLine(args.Result.ToString());
args.Controller.DismissViewController(true, null);
};
this.PresentViewController(mailController, true, null); //this line causes the error
}
}
return true;
}
How can I fix this problem or get around it? This is called from a Xamarin Forms page.
Please take a look at this answer:
Sending e-mail from Gmail in Xamarin.Forms app
besides that you can also do it with this NuGet package:
https://www.nuget.org/packages/Xam.Plugins.Messaging/

Categories