iOS消息推送

iOS消息推送

iOS消息推送分为本地通知和远程推送(APNs)两种。本地通知即常见的闹钟等在App程序内实现的在一定触发条件下执行的通知方式。远程推送即需要通过苹果的APNs服务器向用户设备发送消息。

iOS10苹果推出新的处理推送的框架UserNotifications,这个框架专门用来处理推送通知。
以远程推送为例,在iOS10之前我们接收远程推送是在AppDelegate的方法里面处理,iOS10使用了UserNotifications,在UNUserNotificationCenterDelegate中接收和处理远程推送。
以本地通知为例,iOS10之前我们使用的是UILocalNotification(属于UIKit)这个类处理本地通知,iOS10使用了UserNotifications中新的类UNNotificationContent等实现本地通知。

使用通知,需要请求用户授权

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    //通知对象
    UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
    //设置代理
    notificationCenter.delegate = self;
    //获取用户授权
    [notificationCenter requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionCarPlay completionHandler:^(BOOL granted, NSError * _Nullable error) {
        //回主线程
        dispatch_async(dispatch_get_main_queue(), ^{
            if (!granted) {
                //用户拒绝授权Coding...
            } else {
                //用户已授权,注册远程推送
                [[UIApplication sharedApplication] registerForRemoteNotifications];
            }
        });
    }];
    
    return YES;
}

这里registerForRemoteNotifications是注册远程通知,如果只是使用本地通知则不需要注册。

一、本地通知

添加一个按钮,点击后添加一个本地通知

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    UIButton *sender = [UIButton buttonWithType:UIButtonTypeContactAdd];
    sender.frame = CGRectMake(CGRectGetWidth(self.view.frame)*0.5-50, 100, 64, 32);
    [sender addTarget:self action:@selector(showLocalNotification:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:sender];
}

1、引入框架头文件

#import "ViewController.h"
#import 

2、创建本地通知

- (void)showLocalNotification:(UIButton *)sender {
    //创建本地通知
    UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
    content.title = @"Calendar Title";
    content.subtitle = @"This is subtitle";
    content.body = @"This is body";
    content.userInfo = @{};//这里可以自定义用户数据
    content.sound = [UNNotificationSound defaultSound];
    content.badge = @([UIApplication sharedApplication].applicationIconBadgeNumber+1);
    
    //设置本地通的知触发机制
    UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:10 repeats:NO];
    
    //创建本地通知请求
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"time noti" content:content trigger:trigger];
    
    //创建本地通知对象,设置接收消息代理,
    UNUserNotificationCenter *localNotificationCenter = [UNUserNotificationCenter currentNotificationCenter];
    localNotificationCenter.delegate = self;
    
    //添加本地通知
    [localNotificationCenter addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
        if (error) {
            
        }
    }];
}

10秒后不管应用程序在后台还是在前台还是杀死了应用程序,都会收到aler通知。

2、接收本地通知

接收本地通知主要是通过UNUserNotificationCenterDelegate的方法。

UNUserNotificationCenterDelegate提供了两个代理

这个代理方法当应用程序在前台时,收到本地通知会执行。

- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
        NSLog(@"willPresentNotification");
    NSLog(@"title : %@",notification.request.content.title);
    NSLog(@"subtitle : %@",notification.request.content.subtitle);
    NSLog(@"body : %@",notification.request.content.body);
    NSLog(@"userInfo : %@",notification.request.content.userInfo);
    
    //这个回调可以设置当收到通知后, 有哪些效果呈现(声音/提醒/数字角标)
    completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
}

这个代理方法是当用户点击了alert时会执行

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {

}

有时候点击alert的时候我们需要判断程序是否在前台,并做不同的处理:

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
    
    NSLog(@"applicationState %ld",(long)[UIApplication sharedApplication].applicationState);

    switch ([UIApplication sharedApplication].applicationState) {
        case UIApplicationStateActive:
            //程序在前台时点击了alert会执行这里
            
            break;
        case UIApplicationStateInactive:
            //应用程序运行在前台但不接收事件,程序从后台刚进入前台时的状态。当在后台点击alert进入前台时,会执行这里。
            
            break;
        case UIApplicationStateBackground:
            //应用程序在后台,这种状态在这里捕获不到
            
            break;
    }
    
    completionHandler();
}

2.1 应用程序在前台

收到本地通知会执行第一个代理方法,点击alert会执行第二个代理方法。处理代码时我们需要进行判断,避免多次处理。

2.2 应用程序在后台

应用程序在后台时,只能通过点击alert后,在第二个代理方法中处理代码,第一个代理方法是不会被执行的。

2.3 应用程序被杀死

应用程序被杀死,也是通过点击alert进入应用程序,这时候在通过第二个代理方法,捕获通知,进行处理。

完整代码

#import "ViewController.h"
#import 

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    UIButton *sender = [UIButton buttonWithType:UIButtonTypeContactAdd];
    sender.frame = CGRectMake(CGRectGetWidth(self.view.frame)*0.5-50, 100, 64, 32);
    [sender addTarget:self action:@selector(showLocalNotification:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:sender];
}


- (void)showLocalNotification:(UIButton *)sender {
    //创建本地通知
    UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
    content.title = @"Calendar Title";
    content.subtitle = @"This is subtitle";
    content.body = @"This is body";
    content.userInfo = @{};//这里可以自定义用户数据
    content.sound = [UNNotificationSound defaultSound];
    content.badge = @([UIApplication sharedApplication].applicationIconBadgeNumber+1);
    
    //设置本地通的知触发机制
    UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:10 repeats:NO];
    
    //创建本地通知请求
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"time noti" content:content trigger:trigger];
    
    //创建本地通知对象,设置接收消息代理,
    UNUserNotificationCenter *localNotificationCenter = [UNUserNotificationCenter currentNotificationCenter];
    localNotificationCenter.delegate = self;
    
    //添加本地通知
    [localNotificationCenter addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
        if (error) {
            
        }
    }];
}

#pragma mark - UNUserNotificationCenterDelegate
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
    
    NSLog(@"ViewController willPresentNotification");
    NSLog(@"title : %@",notification.request.content.title);
    NSLog(@"subtitle : %@",notification.request.content.subtitle);
    NSLog(@"body : %@",notification.request.content.body);
    NSLog(@"userInfo : %@",notification.request.content.userInfo);
    
    //这个回调可以设置当收到通知后, 有哪些效果呈现(声音/提醒/数字角标)
    completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
}
//
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
    
    NSLog(@"applicationState %ld",(long)[UIApplication sharedApplication].applicationState);

    switch ([UIApplication sharedApplication].applicationState) {
        case UIApplicationStateActive:
            //程序在前台时点击了alert会执行这里
            
            break;
        case UIApplicationStateInactive:
            //应用程序运行在前台但不接收事件,程序从后台刚进入前台时的状态。当在后台点击alert进入前台时,会执行这里。
            
            break;
        case UIApplicationStateBackground:
            //应用程序在后台,这种状态在这里捕获不到
            
            break;
    }
    
    completionHandler();
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

以上代码将UNUserNotificationCenterDelegate放在了发送本地通知的类里面,不建议这样做,这样做的话当应用程序被杀死的情况下点击aler进入时,捕获不到通知。建议将代理设置到AppDelegate中,这样当程序杀死,点击alert进入时也可以捕获到通知内容。

二、远程推送

远程通知由服务器生成,由APNs发送到用户设备上。

需要开启远程推送功能并配置推送证书。

打开TARGET->Capabilities->push Notifications选项,徽章自动添加一个.entitlements的文件。还需要打开Background Modes下的Remote notifications。

1、请求授权,注册远程通知

以下是iOS10注册远程通知

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    //远程推送对象
    UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
    //设置远程推送代理
    notificationCenter.delegate = self;
    //获取用户授权
    [notificationCenter requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionCarPlay completionHandler:^(BOOL granted, NSError * _Nullable error) {
        //回主线程
        dispatch_async(dispatch_get_main_queue(), ^{
            if (!granted) {
                //用户拒绝授权Coding...
            } else {
                //用户已授权,注册远程推送
                [[UIApplication sharedApplication] registerForRemoteNotifications];
            }
        });
    }];
    
    return YES;
}

如果报如下错误:

You've implemented -[ application:didReceiveRemoteNotification:fetchCompletionHandler:], but you still need to add "remote-notification" to the list of your supported UIBackgroundModes in your Info.plist.

需要打开BackGroundModel,勾选Remote notifications选项。

2、设置deviceToken

如下两个方法里回调注册结果

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken NS_AVAILABLE_IOS(3_0) {
    
    NSString *token = [deviceToken description];
    NSLog(@"description %@", token);
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error NS_AVAILABLE_IOS(3_0) {
    
    NSLog(@"didFailToRegisterForRemoteNotificationsWithError : %@",error);
}

如果注册成功会返回deviceToken,将这个token发送到我们自己的服务器。

3、服务器将消息发送至APNs

4、APNs通过设备标识将推送内容推送到设备

5、接收通知

iOS10以前

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler NS_AVAILABLE_IOS(7_0) {
    
    NSLog(@"didReceiveRemoteNotification : %@",userInfo);
}

iOS10 Apple更新了推送方法,收到通知点击触发的方法- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler已被弃用,使用新的方法如下:

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler {
    completionHandler(UIBackgroundFetchResultNewData);
    NSDictionary * userInfo = response.notification.request.content.userInfo;
    [self pushToNewsDetailViewController:userInfo];
}

iOS10通过代理接收通知消息,这和上面所说的本地通知是一样的,iOS10统一了本地通知和远程推送,使用UserNotifications框架简化了iOS推送的使用。


从以上几个步骤看如果要做一个推送的SDK,类似于极光推送这种,主要的工作还是在服务端,客户端需要将deviceToken发送给服务端,服务端根据苹果的需求做一些工作。

iOS10以前,程序在前台运行时,远程推送不会直接显示alert,需要捕获到通知内容,再做处理。程序在运行和非运行时候捕获通知的方法不同,需要单独处理。

你可能感兴趣的:(#)