[iOS]Push Notification on iOS(3)

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

Created with Raphaël 2.2.0收到遠程Silent通知{"content-available":1}App在前臺?- (void)application:didReceiveRemoteNotification:fetchCompletionHandler:獲取遠程數據彈出通知或直接顯示內容用戶點擊通知App在後臺?- (void)userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:結束- (void)userNotificationCenter:willPresentNotification:withCompletionHandler:喚醒Appyesnoyesno

2.Visible Mode

Created with Raphaël 2.2.0收到遠程通知App在後臺或被Killed?系統彈出通知用戶點擊通知- (void)userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:結束- (void)userNotificationCenter:willPresentNotification:withCompletionHandler:是否彈出通知?yesnoyesno

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.

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章