Created
November 30, 2015 15:31
-
-
Save colbylwilliams/33251a77ceb64c7cbc86 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using UIKit; | |
using Foundation; | |
using System.Collections.Generic; | |
namespace Notifications.iOS | |
{ | |
#region APN Payload Container | |
/* * | |
* List of the keys and expected values of the aps payload. | |
* -- | |
* | |
* alert | |
* - string or dictionary | |
* - If this property is included, the system displays a standard alert. You may specify a | |
* string as the value of alert or a dictionary as its value. If you specify a string, | |
* it becomes the message text of an alert with two buttons: Close and View. If the user | |
* taps View, the app is launched. | |
* Alternatively, you can specify a dictionary as the value of alert. | |
* | |
* badge | |
* - number | |
* - The number to display as the badge of the app icon. | |
* If this property is absent, the badge is not changed. To remove the badge, set the | |
* value of this property to 0. | |
* | |
* sound | |
* - string | |
* - The name of a sound file in the app bundle or in the Library/Sounds folder of the app’s | |
* data container. The sound in this file is played as an alert. If the sound file doesn’t | |
* exist or default is specified as the value, the default alert sound is played. The | |
* audio must be in one of the audio data formats that are compatible with system sounds; | |
* see Preparing Custom Alert Sounds for details. | |
* | |
* content-available | |
* - number | |
* - Provide this key with a value of 1 to indicate that new content is available. Including | |
* this key and value means that when your app is launched in the background or resumed, | |
* application:didReceiveRemoteNotification:fetchCompletionHandler: is called. | |
* | |
* category | |
* - string | |
* - Provide this key with a string value that represents the identifier property of the | |
* UIMutableUserNotificationCategory object you created to define custom actions. | |
* | |
* | |
* | |
* * Keys and expected values for the alert dictionary include: | |
* * -- | |
* * | |
* * title | |
* * - string | |
* * - A short string describing the purpose of the notification. Apple Watch displays this | |
* * string as part of the notification interface. This string is displayed only briefly and | |
* * should be crafted so that it can be understood quickly. This key was added in iOS 8.2. | |
* * | |
* * body | |
* * - string | |
* * - The text of the alert message. | |
* * | |
* * title-loc-key | |
* * - string or null | |
* * - The key to a title string in the Localizable.strings file for the current localization. | |
* * The key string can be formatted with %@ and %n$@ specifiers to take the variables | |
* * specified in the title-loc-args array. This key was added in iOS 8.2. | |
* * | |
* * title-loc-args | |
* * - array of strings or null | |
* * - Variable string values to appear in place of the format specifiers in title-loc-key. | |
* * See Localized Formatted Strings for more information. This key was added in iOS 8.2. | |
* * | |
* * action-loc-key | |
* * - string or null | |
* * - If a string is specified, the system displays an alert that includes the Close and View | |
* * buttons. The string is used as a key to get a localized string in the current | |
* * localization to use for the right button’s title instead of “View”. | |
* * | |
* * loc-key | |
* * - string | |
* * - A key to an alert-message string in a Localizable.strings file for the current | |
* * localization (which is set by the user’s language preference). The key string can be | |
* * formatted with %@ and %n$@ specifiers to take the variables specified in the loc-args | |
* * array. | |
* * | |
* * loc-args | |
* * - array of strings | |
* * - Variable string values to appear in place of the format specifiers in loc-key. | |
* * | |
* * launch-image | |
* * - string | |
* * - The filename of an image file in the app bundle; it may include the extension or omit | |
* * it. The image is used as the launch image when users tap the action button or move the | |
* * action slider. If this property is not specified, the system either uses the previous | |
* * snapshot,uses the image identified by the UILaunchImageFile key in the app’s Info.plist | |
* * file, or falls back to Default.png. This property was added in iOS 4.0. | |
* * | |
* */ | |
public class ApnPayload | |
{ | |
public string alertString { get; set; } | |
public ApnPayloadAlert alertDict { get; set; } | |
public int badge { get; set; } | |
public string sound { get; set; } | |
public bool content_available { get; set; } | |
public string category { get; set; } | |
public ApnPayload (NSDictionary payload) | |
{ | |
alertString = payload.ValueForKey(ApnPayloadKeys.alert)?.ToString(); | |
alertDict = new ApnPayloadAlert (payload.ValueForKey(ApnPayloadKeys.alert) as NSDictionary); | |
badge = Convert.ToInt32(payload.ValueForKey(ApnPayloadKeys.badge) as NSNumber); | |
sound = payload.ValueForKey(ApnPayloadKeys.sound)?.ToString(); | |
content_available = Convert.ToInt32(payload.ValueForKey(ApnPayloadKeys.content_available) as NSNumber) == 1; | |
category = payload.ValueForKey(ApnPayloadKeys.category)?.ToString(); | |
} | |
} | |
public class ApnPayloadAlert | |
{ | |
public string body { get; set; } | |
public string show_view { get; set; } | |
public string title { get; set; } | |
public List<string> title_loc_key { get; set; } | |
public string action_loc_key { get; set; } | |
public string loc_key { get; set; } | |
public List<string> loc_args { get; set; } | |
public string launch_image { get; set; } | |
public ApnPayloadAlert (NSDictionary alertDict) | |
{ | |
if (alertDict != null) { | |
body = alertDict.ValueForKey(ApnPayloadKeys.Alert.body)?.ToString(); | |
show_view = alertDict.ValueForKey(ApnPayloadKeys.Alert.show_view)?.ToString(); | |
title = alertDict.ValueForKey(ApnPayloadKeys.Alert.title)?.ToString(); | |
action_loc_key = alertDict.ValueForKey(ApnPayloadKeys.Alert.action_loc_key)?.ToString(); | |
loc_key = alertDict.ValueForKey(ApnPayloadKeys.Alert.loc_key)?.ToString(); | |
launch_image = alertDict.ValueForKey(ApnPayloadKeys.Alert.launch_image)?.ToString(); | |
title_loc_key = new List<string> (); | |
var title_loc_key_arr = alertDict.ValueForKey(ApnPayloadKeys.Alert.title_loc_key) as NSArray; | |
for (nuint i = 0; i < title_loc_key_arr.Count; i++) { | |
title_loc_key.Add(title_loc_key_arr.GetItem<NSString>(i)?.ToString()); | |
} | |
loc_args = new List<string> (); | |
var loc_args_arr = alertDict.ValueForKey(ApnPayloadKeys.Alert.loc_args) as NSArray; | |
for (nuint i = 0; i < loc_args_arr.Count; i++) { | |
loc_args.Add(loc_args_arr.GetItem<NSString>(i)?.ToString()); | |
} | |
} | |
} | |
} | |
#endregion | |
#region APN Payload Container | |
public static class ApnPayloadKeys | |
{ | |
public static NSString payload = new NSString ("aps"); | |
public static NSString alert = new NSString ("alert"); | |
public static NSString badge = new NSString ("badge"); | |
public static NSString sound = new NSString ("sound"); | |
public static NSString content_available = new NSString ("content-available"); | |
public static NSString category = new NSString ("category"); | |
public static class Alert | |
{ | |
public static NSString body = new NSString ("body"); | |
public static NSString show_view = new NSString ("show-view"); | |
public static NSString title = new NSString ("title"); | |
public static NSString title_loc_key = new NSString ("title-loc-key"); | |
public static NSString action_loc_key = new NSString ("action-loc-key"); | |
public static NSString loc_key = new NSString ("loc-key"); | |
public static NSString loc_args = new NSString ("loc-args"); | |
public static NSString launch_image = new NSString ("launch-image"); | |
} | |
} | |
#endregion | |
public class NotificationAppDelegate : UIApplicationDelegate | |
{ | |
nint BackgroundTaskId; | |
static readonly bool iOS8 = UIDevice.CurrentDevice.CheckSystemVersion(8, 0); | |
public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions) | |
{ | |
/* Wrap everything in a try/catch, otherwise when the app receives a "silent" notification | |
* and is launched into the background, then crashes immediately, you'll never know, as | |
* it crashes before DidReceiveRemoteNotification can be called. | |
* This happens often when the user is doing something with the UI here without checking | |
* whether or the app was launched into the background | |
* */ | |
try { | |
if (launchOptions != null) { | |
// check for a local notification | |
if (launchOptions.ContainsKey(UIApplication.LaunchOptionsLocalNotificationKey)) { | |
var localNotification = launchOptions[UIApplication.LaunchOptionsLocalNotificationKey] as UILocalNotification; | |
if (localNotification != null) { | |
Console.WriteLine(localNotification); | |
} | |
// reset our badge | |
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0; | |
} | |
// check for a remote notification (DidReceiveRemoteNotification will still get called) | |
if (launchOptions.ContainsKey(UIApplication.LaunchOptionsRemoteNotificationKey)) { | |
var remoteNotification = launchOptions[UIApplication.LaunchOptionsRemoteNotificationKey] as NSDictionary; | |
if (remoteNotification != null) { | |
var notificationPayload = new ApnPayload (remoteNotification); | |
Console.WriteLine(notificationPayload); | |
// reset our badge | |
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0; | |
} | |
} | |
} | |
registerForRemoteNotifications(); | |
// application.SetMinimumBackgroundFetchInterval(UIApplication.BackgroundFetchIntervalMinimum); | |
} catch (Exception ex) { | |
Console.WriteLine(ex.Message); | |
throw ex; | |
} | |
return true; | |
} | |
#region Register for Notifications | |
static void registerForRemoteNotifications () | |
{ | |
if (iOS8) { | |
/* The notification types represent the user interface elements the app displays when | |
* it receives a notification: badging the app’s icon, playing a sound, and displaying | |
* an alert. If you don’t register any notification types, the system pushes all remote | |
* notifications to your app silently, that is, without displaying any user interface. | |
* */ | |
var notificationSettings = UIUserNotificationSettings.GetSettingsForTypes(UIUserNotificationType.Sound | UIUserNotificationType.Alert | UIUserNotificationType.Badge, null); | |
UIApplication.SharedApplication.RegisterUserNotificationSettings(notificationSettings); | |
UIApplication.SharedApplication.RegisterForRemoteNotifications(); | |
} else { | |
UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge); | |
} | |
} | |
// Successfully registered with Apple Push Notification service (APNs). | |
public override void RegisteredForRemoteNotifications (UIApplication application, NSData deviceToken) | |
{ | |
/* application | |
* The app object that initiated the remote-notification registration process. | |
* | |
* deviceToken | |
* A token that identifies the device to APNs. The token is an opaque data | |
* type because that is the form that the provider needs to submit to the APNs servers | |
* when it sends a notification to a device. The APNs servers require a binary format | |
* for performance reasons. | |
* The size of a device token is 32 bytes. | |
* Note that the device token is different from the uniqueIdentifier property of UIDevice | |
* because, for security and privacy reasons, it must change when the device is wiped. | |
* */ | |
var token = deviceToken.ToString(); | |
Console.WriteLine("Successfully registered for Remote Notifications :\n{0}", token); | |
/* Send this token to your server along with any other information about the user you want | |
* to be associated with this device. When you want to send a push notification to this | |
* user, you'll use this token to identify them. | |
* */ | |
} | |
// Failed to successfully register with Apple Push Notification service (APNs). | |
public override void FailedToRegisterForRemoteNotifications (UIApplication application, NSError error) | |
{ | |
/* application | |
* The app object that initiated the remote-notification registration process. | |
* | |
* error | |
* An NSError object that encapsulates information why registration did not succeed. | |
* The app can choose to display this information to the user. | |
* Process the error object appropriately and make sure you disable any logic within the | |
* app that depends on receiving remote notifications. You don't want do any unnecessary | |
* processing within the app for notifications that aren't going to be coming in. Just | |
* gracefully degrade. | |
* */ | |
Console.WriteLine("Error registering for Remote Notifications :\n{0}", error.LocalizedDescription); | |
} | |
#endregion | |
#region Handle Notifications | |
// Currently running app receives a local notification. | |
public override void ReceivedLocalNotification (UIApplication application, UILocalNotification notification) | |
{ | |
/* application | |
* The app object that received the local notification. | |
* | |
* notification | |
* A local notification that encapsulates details about the notification, potentially | |
* including custom data. | |
* | |
* completionHandler | |
* The block to execute when the download operation is complete. When calling this block, | |
* pass in the fetch result value that best describes the results of your download | |
* operation. You must call this handler and should do so as soon as possible. For a list | |
* of possible values, see the UIBackgroundFetchResult type. | |
* | |
* | |
* Discussion | |
* | |
* As soon as you finish processing the notification, you must call the block in the | |
* handler parameter or your app will be terminated. Your app has up to 30 seconds of | |
* wall-clock time to process the notification and call the specified completion handler | |
* block. In practice, you should call the handler block as soon as you are done | |
* processing the notification. | |
* | |
* The system tracks the elapsed time, power usage, and | |
* data costs for your app’s background downloads. Apps that use significant amounts of | |
* power when processing remote notifications may not always be woken up early to process | |
* future notifications. | |
* */ | |
if (notification != null) { | |
Console.WriteLine(notification); | |
} | |
// reset our badge | |
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0; | |
} | |
// Implement DidReceiveRemoteNotification method instead of this one whenever possible. If | |
// the delegate implements both methods, the app object calls DidReceiveRemoteNotification | |
public override void ReceivedRemoteNotification (UIApplication application, NSDictionary userInfo) | |
{ | |
/* application | |
* Your singleton app object. | |
* | |
* userInfo | |
* A dictionary that contains information related to the remote notification, potentially | |
* including a badge number for the app icon, an alert sound, an alert message to display | |
* to the user, a notification identifier, and custom data. The provider originates it as | |
* a JSON-defined dictionary that iOS converts to an NSDictionary object; the dictionary | |
* may contain only property-list objects plus NSNull. | |
* | |
* */ | |
} | |
// A remote notification arrived that indicates there is data to be fetched. | |
public override void DidReceiveRemoteNotification (UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler) | |
{ | |
/* application | |
* Your singleton app object. | |
* | |
* userInfo | |
* A dictionary that contains information related to the remote notification, potentially | |
* including a badge number for the app icon, an alert sound, an alert message to display | |
* to the user, a notification identifier, and custom data. The provider originates it as | |
* a JSON-defined dictionary that iOS converts to an NSDictionary object; the dictionary | |
* may contain only property-list objects plus NSNull. | |
* | |
* completionHandler | |
* The block to execute when the download operation is complete. When calling this block, | |
* pass in the fetch result value that best describes the results of your download | |
* operation. You must call this handler and should do so as soon as possible. For a list | |
* of possible values, see the UIBackgroundFetchResult type. | |
* | |
* | |
* Discussion | |
* | |
* Use this method to process incoming remote notifications for your app. Unlike the | |
* ReceivedRemoteNotification method, which is called only when your app is running in | |
* the foreground, the system calls this method when your app is running in the foreground | |
* or background. In addition, if you enabled the remote notifications background mode, | |
* the system launches your app (or wakes it from the suspended state) and puts it in the | |
* background state when a remote notification arrives. However, the system does not | |
* automatically launch your app if the user has force-quit it. In that situation, the | |
* user must relaunch your app or restart the device before the system attempts to launch | |
* your app automatically again. | |
* | |
* | NOTE: If the user opens your app from the system-displayed alert, the system may | |
* | call this method again when your app is about to enter the foreground so that you | |
* | can update your user interface and display information pertaining to the notification. | |
* | |
* When a remote notification arrives, the system displays the notification to the user | |
* and launches the app in the background (if needed) so that it can call this method. | |
* Launching your app in the background gives you time to process the notification and | |
* download any data associated with it, minimizing the amount of time that elapses | |
* between the arrival of the notification and displaying that data to the user. | |
* | |
* As soon as you finish processing the notification, you must call the block in the | |
* handler parameter or your app will be terminated. Your app has up to 30 seconds of | |
* wall-clock time to process the notification and call the specified completion handler | |
* block. In practice, you should call the handler block as soon as you are done | |
* processing the notification. | |
* | |
* The system tracks the elapsed time, power usage, and | |
* data costs for your app’s background downloads. Apps that use significant amounts of | |
* power when processing remote notifications may not always be woken up early to process | |
* future notifications. | |
* */ | |
try { | |
switch (application.ApplicationState) { | |
case UIApplicationState.Active: | |
break; | |
case UIApplicationState.Inactive: | |
break; | |
case UIApplicationState.Background: | |
break; | |
} | |
// check for a remote notification (DidReceiveRemoteNotification will still get called) | |
if (userInfo.ContainsKey(UIApplication.LaunchOptionsRemoteNotificationKey)) { | |
var remoteNotification = userInfo[UIApplication.LaunchOptionsRemoteNotificationKey] as NSDictionary; | |
if (remoteNotification != null) { | |
var notificationPayload = new ApnPayload (remoteNotification); | |
Console.WriteLine(notificationPayload); | |
// reset our badge | |
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0; | |
} | |
} | |
Console.WriteLine("Perform whatever background fetching you need to do"); | |
var dataToFetch = true; | |
if (dataToFetch) { | |
// await fetch data | |
completionHandler(UIBackgroundFetchResult.NewData); | |
} else { | |
completionHandler(UIBackgroundFetchResult.NoData); | |
} | |
} catch (Exception ex) { | |
Console.WriteLine(ex.Message); | |
// you *must* call this at some point | |
completionHandler(UIBackgroundFetchResult.Failed); | |
} | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment