Develop Push Notification on iOS
Configure Push Notification Service
Support Silent Mode
Xcode | Targets | Capabilities | Background Modes | Remote Notification turn on
Development
Notification dispatch flow
1.Silent Mode
2.Visible Mode
Payload
Silent Mode
{"content-available":1}
It wakes your app in the background and gives it time to initiate downloads from your server and update its content.
Important
The system treats silent notifications as low-priority. You can use them to refresh your app’s content, but the system doesn’t guarantee their delivery. In addition, the delivery of silent notifications may be throttled if the total number becomes excessive. The actual number of silent notifications allowed by the system depends on current conditions, but don’t try to send more than two or three silent notifications per hour.
{
"aps" : {
"content-available" : 1
},
"acme1" : "bar",
"acme2" : 42
}
- App in Background - Wake up App
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
NSLog(@"user info %@,%s",userInfo,__FUNCTION__);
[self sendNotificationToUI:userInfo
tag:[NSString stringWithFormat:@"%s",__FUNCTION__]];
if (completionHandler) {
UIBackgroundFetchResult result = UIBackgroundFetchResultNewData;
completionHandler(result);
}
}
Your app has up to 30 seconds of wall-clock time to process the notification and call the specified completion handler block
- App in Foreground
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
NSLog(@"user info %@,%s",userInfo,__FUNCTION__);
[self sendNotificationToUI:userInfo
tag:[NSString stringWithFormat:@"%s",__FUNCTION__]];
if (completionHandler) {
UIBackgroundFetchResult result = UIBackgroundFetchResultNewData;
completionHandler(result);
}
}
Your app has up to 30 seconds of wall-clock time to process the notification and call the specified completion handler block
- App Killed
Not to relaunch APP.
Visible Mode
-
App in Background
Displayed to users directly.
When user clicked the notification,the following callback invoked. (open app/dismiss/UNNotificationAction)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
NSLog(@"didReceiveNotificationResponse user response %@",response);
}
- App in Foreground
Not displayed to users directly.App can display it when needed(UNNotificationPresentationOptions).
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
NSLog(@"willPresentNotification notification %@",notification);
if (completionHandler) {
UNNotificationPresentationOptions options = UNNotificationPresentationOptionAlert;
completionHandler(options);
}
}
Once user clicked notification, it will also call
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
NSLog(@"didReceiveNotificationResponse user response %@",response);
}
- App in Killed
Display notification directly, will wake up App once user clicked the notification.
PushNotificationServiceExtension
Change Push Notification before displayed before Users.
{"mutable-content":1}
- Modifying Content in Newly Delivered Notifications
Can App have a chance to handle the notification in different mode when notification arrived?
Mode | Background | Foreground |
---|---|---|
Slient | YES | YES |
Visible | NO | YES |
PushNotificationContentExtension
Customized Notification View(TBD)
Declaring Your Actionable Notification Types
FAQ
1.Remote notification support is unavailable due to error: Error Domain=NSCocoaErrorDomain Code=3000 “未找到应用程序的“aps-environment”的授权字符串”
Add “APS Environment” in entitlements file and set value to “development”
2. How to view content of mobileprovision file?
security cms -D -i MM_iOS_Development.mobileprovision
More details:
Inside Code Signing
Reference
Sample Code
//
// AppDelegate.h
// PushNotificationDemo
#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate,UNUserNotificationCenterDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
//
// AppDelegate.m
// PushNotificationDemo
//
#import "AppDelegate.h"
#import "UserNotification/PushNotificationHandler.h"
@interface AppDelegate ()
@property (nonatomic,strong) PushNotificationHandler *pushNotificationHandler;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//Push Notification
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
self.pushNotificationHandler = [PushNotificationHandler handler];
[self.pushNotificationHandler application:application didFinishLaunchingWithOptions:launchOptions];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
#pragma mark Push Notification
// Handle remote notification registration.
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {
[self.pushNotificationHandler application:app didRegisterForRemoteNotificationsWithDeviceToken:devToken];
}
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
// The token is not currently available.
[self.pushNotificationHandler application:app didFailToRegisterForRemoteNotificationsWithError:err];
}
#pragma mark UNUserNotificationCenterDelegate
// The method will be called on the delegate only if the application is in the foreground. If the method is not implemented or the handler is not called in a timely manner then the notification will not be presented. The application can choose to have the notification presented as a sound, badge, alert and/or in the notification list. This decision should be based on whether the information in the notification is otherwise visible to the user.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
[self.pushNotificationHandler userNotificationCenter:center willPresentNotification:notification withCompletionHandler:completionHandler];
}
// The method will be called on the delegate when the user responded to the notification by opening the application, dismissing the notification or choosing a UNNotificationAction. The delegate must be set before the application returns from application:didFinishLaunchingWithOptions:.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
[self.pushNotificationHandler userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler];
}
// The method will be called on the delegate when the application is launched in response to the user's request to view in-app notification settings. Add UNAuthorizationOptionProvidesAppNotificationSettings as an option in requestAuthorizationWithOptions:completionHandler: to add a button to inline notification settings view and the notification settings view in Settings. The notification will be nil when opened from Settings.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(nullable UNNotification *)notification {
[self.pushNotificationHandler userNotificationCenter:center openSettingsForNotification:notification];
}
/*! This delegate method offers an opportunity for applications with the "remote-notification" background mode to fetch appropriate new data in response to an incoming remote notification. You should call the fetchCompletionHandler as soon as you're finished performing that operation, so the system can accurately estimate its power and data cost.
This method will be invoked even if the application was launched or resumed because of the remote notification. The respective delegate methods will be invoked first. Note that this behavior is in contrast to application:didReceiveRemoteNotification:, which is not called in those cases, and which will not be invoked if this method is implemented. !*/
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
[self.pushNotificationHandler application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
@end
//
// ViewController.h
// PushNotificationDemo
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UILabel *infoLabel;
@property (weak, nonatomic) IBOutlet UIButton *sendButton;
@end
//
// ViewController.m
// PushNotificationDemo
//
#import "ViewController.h"
#import "UserNotification/PushNotificationUtils.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *title = NSLocalizedStringFromTable(@"SENDLOCALNOTIFICATION", @"PushNotification", nil);
[self.sendButton setTitle:title forState:UIControlStateNormal];
[self registerObservers];
}
- (void) dealloc {
[self removeObservers];
}
- (void) registerObservers {
__weak typeof(self) weakSelf = self;
NSOperationQueue *operationQueue = [[NSOperationQueue alloc]init];
[[NSNotificationCenter defaultCenter] addObserverForName:@"PushNotificationDemo" object:nil queue:operationQueue usingBlock:^(NSNotification *note) {
NSLog(@"%@",note.name);
NSString *tag = [note.userInfo objectForKey:@"tag"];
NSString *objectDescription = [note.object description];
NSString *text = [NSString stringWithFormat:@"1.Called\n %@\n\n2.Content:\n%@",tag,objectDescription];
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.infoLabel.text = text;
});
}];
}
- (void) removeObservers {
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"PushNotificationDemo" object:nil];
}
- (void)sendLocalNotification {
[PushNotificationUtils sendLocalNotification];
}
- (IBAction)onSendLocalNotificationClicked:(UIButton *)sender {
[self sendLocalNotification];
}
@end
//
// PushNotificationUtils.h
// PushNotificationDemo
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface PushNotificationUtils : NSObject
+ (void)sendLocalNotification ;
@end
NS_ASSUME_NONNULL_END
//
// PushNotificationUtils.m
// PushNotificationDemo
//
#import "PushNotificationUtils.h"
#import <UserNotifications/UserNotifications.h>
@implementation PushNotificationUtils
+ (void)sendLocalNotification {
// Configure the notification's payload.
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
content.title = [NSString localizedUserNotificationStringForKey:@"PUSH_TITLE" arguments:nil];
content.body = [NSString localizedUserNotificationStringForKey:@"PUSH_BODY"
arguments:nil];
content.sound = [UNNotificationSound defaultSound];
// Deliver the notification in five seconds.
UNTimeIntervalNotificationTrigger* trigger = [UNTimeIntervalNotificationTrigger
triggerWithTimeInterval:1 repeats:NO];
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"YOURBUNDLEID"
content:content trigger:trigger];
// Schedule the notification.
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
NSLog(@"%@",error);
}];
}
@end
//
// PushNotificationHandler.h
// PushNotificationDemo
//
#import <Foundation/Foundation.h>
#import "AppDelegate.h"
NS_ASSUME_NONNULL_BEGIN
@interface PushNotificationHandler : NSObject
+ (instancetype) handler ;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken ;
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err ;
#pragma mark UNUserNotificationCenterDelegate
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler ;
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler ;
- (void)userNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(nullable UNNotification *)notification ;
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler ;
@end
NS_ASSUME_NONNULL_END
//
// PushNotificationHandler.m
// PushNotificationDemo
#import "PushNotificationHandler.h"
#import "PushNotificationUtils.h"
@implementation PushNotificationHandler
+ (instancetype) handler {
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[[self class] alloc] init];
});
return instance;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Configure the user interactions first.
UNAuthorizationOptions options = UNAuthorizationOptionBadge
| UNAuthorizationOptionSound
| UNAuthorizationOptionProvidesAppNotificationSettings
| UNAuthorizationOptionAlert;
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError * _Nullable error) {
}];
// Register for remote notifications.
[[UIApplication sharedApplication] registerForRemoteNotifications];
return YES;
}
#pragma mark Push Notification
// Handle remote notification registration.
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {
// Forward the token to your provider, using a custom method.
// [self enableRemoteNotificationFeatures];
// [self forwardTokenToServer:devTokenBytes];
NSString * token = [[[[devToken description]
stringByReplacingOccurrencesOfString: @"<" withString: @""]
stringByReplacingOccurrencesOfString: @">" withString: @""]
stringByReplacingOccurrencesOfString: @" " withString: @""];
NSLog(@"device token is %@", token);
}
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
// The token is not currently available.
NSLog(@"Remote notification support is unavailable due to error: %@", err);
// [self disableRemoteNotificationFeatures];
}
#pragma mark UNUserNotificationCenterDelegate
// The method will be called on the delegate only if the application is in the foreground. If the method is not implemented or the handler is not called in a timely manner then the notification will not be presented. The application can choose to have the notification presented as a sound, badge, alert and/or in the notification list. This decision should be based on whether the information in the notification is otherwise visible to the user.
//Foreground
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
NSLog(@"willPresentNotification notification %@,%s",notification,__FUNCTION__);
[self sendNotificationToUI:notification.request.content.userInfo
tag:[NSString stringWithFormat:@"%s",__FUNCTION__]];
if (completionHandler) {
UNNotificationPresentationOptions options = UNNotificationPresentationOptionAlert;
completionHandler(options);
}
}
// The method will be called on the delegate when the user responded to the notification by opening the application, dismissing the notification or choosing a UNNotificationAction. The delegate must be set before the application returns from application:didFinishLaunchingWithOptions:.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
[self sendNotificationToUI:response.notification.request.content.userInfo
tag:[NSString stringWithFormat:@"%s",__FUNCTION__]];
NSLog(@"didReceiveNotificationResponse user response %@,%s",response,__FUNCTION__);
if (completionHandler) {
completionHandler();
}
// if ([response.notification.request.content.categoryIdentifier isEqualToString:@"TIMER_EXPIRED"]) {
// // Handle the actions for the expired timer.
// if ([response.actionIdentifier isEqualToString:@"SNOOZE_ACTION"])
// {
// // Invalidate the old timer and create a new one. . .
// }
// else if ([response.actionIdentifier isEqualToString:@"STOP_ACTION"])
// {
// // Invalidate the timer. . .
// }
//
// }
// Else handle actions for other notification types. . .
}
// The method will be called on the delegate when the application is launched in response to the user's request to view in-app notification settings. Add UNAuthorizationOptionProvidesAppNotificationSettings as an option in requestAuthorizationWithOptions:completionHandler: to add a button to inline notification settings view and the notification settings view in Settings. The notification will be nil when opened from Settings.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(nullable UNNotification *)notification {
[self sendNotificationToUI:notification
tag:[NSString stringWithFormat:@"%s",__FUNCTION__]];
NSLog(@"openSettingsForNotification %@,%s",notification,__FUNCTION__);
}
/*! This delegate method offers an opportunity for applications with the "remote-notification" background mode to fetch appropriate new data in response to an incoming remote notification. You should call the fetchCompletionHandler as soon as you're finished performing that operation, so the system can accurately estimate its power and data cost.
This method will be invoked even if the application was launched or resumed because of the remote notification. The respective delegate methods will be invoked first. Note that this behavior is in contrast to application:didReceiveRemoteNotification:, which is not called in those cases, and which will not be invoked if this method is implemented. !*/
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
NSLog(@"user info %@,%s",userInfo,__FUNCTION__);
[self sendNotificationToUI:userInfo
tag:[NSString stringWithFormat:@"%s",__FUNCTION__]];
if (completionHandler) {
UIBackgroundFetchResult result = UIBackgroundFetchResultNewData;
completionHandler(result);
}
[PushNotificationUtils sendLocalNotification];
}
- (void) sendNotificationToUI:(NSObject *)object
tag:(NSString *)tag
{
NSNotification *notification = [[NSNotification alloc] initWithName:@"PushNotificationDemo" object:object userInfo:@{@"Status": @"Success",@"tag":tag}];
[[NSNotificationCenter defaultCenter] postNotification:notification];
}
@end