This is Xamarin C# code (different syntax and capitalization than Objective-C, but I think it is translatable line-by-line to Objective-C).
Tested on both iOS 9.3 and iOS 10.2.
To initialize "local" and "remote" notifications:
// "UIApplicationDelegate" is for "local" notifications,
// "IUNUserNotificationCenterDelegate, IMessagingDelegate" for "remote" notifications.
public class AppDelegate : UIApplicationDelegate,
IUNUserNotificationCenterDelegate, IMessagingDelegate
{
...
public override bool FinishedLaunching( UIApplication application, NSDictionary launchOptions )
{
...
RegisterForOurRemoteNotifications( this );
RegisterForOurLocalNotifications();
...
}
...
// --- Comment out if not using Google FCM. ---
public override void RegisteredForRemoteNotifications( UIApplication application, NSData deviceToken )
{
//base.RegisteredForRemoteNotifications( application, deviceToken );
Firebase.InstanceID.InstanceId.SharedInstance.SetApnsToken( deviceToken,
Firebase.InstanceID.ApnsTokenType.Sandbox );
}
...
// ----- "static"; Could be in another class. -----
// These flags are for our convenience, so we know initialization was done.
static bool IsRegisteredForNotifications;
static bool IsRegisteredForRemoteNotifications;
// Optional - true when we are using Google "Firebase Cloud Messaging".
static bool HasFCM;
public static void RegisterForOurRemoteNotifications( AppDelegate del )
{
// Google "Firebase Cloud Messaging" (FCM) Monitor token generation
// (Uncomment, if you are using FCM for notifications.)
//InstanceId.Notifications.ObserveTokenRefresh( TokenRefreshNotification );
if (UIDevice.CurrentDevice.CheckSystemVersion( 10, 0 )) {
// iOS 10 or later
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 = del;
// For iOS 10 data message (sent via Google FCM).
// (Uncomment, if you are using FCM for notifications.)
// TBD: If NOT using FCM, you may need some other lines of code here.
//Messaging.SharedInstance.RemoteMessageDelegate = del;
} 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();
IsRegisteredForRemoteNotifications = true;
// Uncomment if using Google "Firebase Cloud Messaging" (FCM).
//TokenRefreshNotification( null, null );
//if (UIDevice.CurrentDevice.CheckSystemVersion( 9, 0 )) // Needed to call this twice on iOS 9 for some reason.
// TokenRefreshNotification( null, null );
UIApplication.SharedApplication.SetMinimumBackgroundFetchInterval( UIApplication.BackgroundFetchIntervalMinimum );
}
public static void RegisterForOurLocalNotifications()
{
// --- Our app's notification actions. ---
UNNotificationAction followAction = UNNotificationAction.FromIdentifier( "follow", PS.LocalizedString( "Follow" ), UNNotificationActionOptions.None );
UNNotificationAction likeAction = UNNotificationAction.FromIdentifier( "like", PS.LocalizedString( "Like" ), UNNotificationActionOptions.None );
// ...
// --- Our app's notification categories ---
UNNotificationCategory followCategory = UNNotificationCategory.FromIdentifier( "followCategory", new UNNotificationAction[] { followAction, likeAction },
new string[] { }, UNNotificationCategoryOptions.None );
// ...
// --- All of the app's categories from above ---
var categories = new UNNotificationCategory[] { followCategory /*, ...*/ };
// --- Same for all apps ---
UIUserNotificationSettings settings = UIUserNotificationSettings.GetSettingsForTypes(
UIUserNotificationType.Alert |
UIUserNotificationType.Badge |
UIUserNotificationType.Sound
, new NSSet( categories ) );
UIApplication.SharedApplication.RegisterUserNotificationSettings( settings );
if (UIDevice.CurrentDevice.CheckSystemVersion( 10, 0 )) {
UNUserNotificationCenter.Current.SetNotificationCategories( new NSSet<UNNotificationCategory>( categories ) );
UNUserNotificationCenter.Current.RequestAuthorization( UNAuthorizationOptions.Alert | UNAuthorizationOptions.Sound | UNAuthorizationOptions.Badge,
( result, err ) => {
Console.WriteLine( result.ToString() );
} );
}
IsRegisteredForNotifications = true;
}
}
// -------------------------------------------------------
// --- These are for Google "Firebase Cloud Messaging" ---
// (Comment out if not using FCM.)
public static string Token;
static void TokenRefreshNotification( object sender, NSNotificationEventArgs e )
{
// This method will be fired every time a new token is generated, including the first
// time. So if you need to retrieve the token as soon as it is available this is where that
// should be done.
//var refreshedToken = InstanceId.SharedInstance.Token;
ConnectToFCM( UIApplication.SharedApplication.KeyWindow.RootViewController );
// TODO: If necessary send token to application server.
}
public static void ConnectToFCM( UIViewController fromViewController )
{
Messaging.SharedInstance.Connect( error => {
if (error != null) {
Helper.logD( "Unable to connect to FCM", error.LocalizedDescription );
} else {
//var options = new NSDictionary();
//options.SetValueForKey( DeviceToken, Constants.RegisterAPNSOption );
//options.SetValueForKey( new NSNumber( true ), Constants.APNSServerTypeSandboxOption );
//InstanceId.SharedInstance.GetToken("", InstanceId.ScopeFirebaseMessaging
Token = InstanceId.SharedInstance.Token;
Console.WriteLine( $"Token: {InstanceId.SharedInstance.Token}" );
HasFCM = true;
}
} );
}
// ------------------ End Google FCM ---------------------
// -------------------------------------------------------
}
The above code initializes your app so that it can receive notifications.
IMPORTANT: You also need to set appropriate permissions on your app; see Apple docs, or links mentioned in the question. And you need this file:
Entitlements.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>
<string>
above must contain either "development" or "production". (I don't know the significance of our app still saying "development" here; I haven't examined what is built to see if it gets automatically changed to "production" by Xcode before submission to Apple. According to https://stackoverflow.com/a/40857877/199364 it does.)
Then you need code to send [e.g. your app tells your server to notify your friends' devices of what you are doing now] and receive a local or remote notification. That code, in our app, is combined with our specific notification actions and categories; I do not have time to extract a concise version to post here. Please see Apple docs, or links mentioned in the original question, for full details.
Here are the essential methods (Add to class AppDelegate
above) to receive notifications:
public override void ReceivedLocalNotification( UIApplication application, UILocalNotification notification )
{
...
}
public override void DidReceiveRemoteNotification( UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler )
{
...
}
[Export( "userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:" )]
public void DidReceiveNotificationResponse( UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler )
{
...
}
Other methods that you may want/need to override or implement (also see the interfaces declared on class AppDelegate
above); some of these might be specific to FCM:
ApplicationReceivedRemoteMessage
ReceivedRemoteNotification
WillPresentNotification
PerformFetch (for background notifications)
HandleAction