[TOC]
一、工作机制:
APNS 是 Apple Push Notification Service 的缩写,是苹果服务器。
工作流程如下所示:
- 首先app向iOS注册推送消息
- iOS收到app的注册后,向APNS Server索要deviceToken,app接收返回的deviceToken
- app把接收到的deviceToken传给我们的服务器
- 当我们的服务器需要推送消息时,就把要推送的消息和deviceToken发送给APNS Server
- APNS Server服务将推送的消息发送给app
二、app代码实现:
1、注册消息推送:
#import <UserNotifications/UserNotifications.h>
// 注册APNS推送
- (void)registerRemoteNotification {
if (@available(iOS 10.0, *)) {
UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
// 必须写代理,不然无法监听通知的接收与点击事件
notificationCenter.delegate = self;
// 请求授权
[notificationCenter requestAuthorizationWithOptions:UNAuthorizationOptionAlert
completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted && !error) {
// 用户同意授权,注册远程推送
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
// 用户不同意授权
}
}];
// 获取用户同意的或者更改的推送权限设置
[notificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
NSLog(@"%@",settings);
}];
} else {
// iOS8 ~ iOS10
if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
} else {
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert];
}
}
}
2、把拿到到deviceToken传给我们的服务器:
// 获取deviceToken成功
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// 把deviceToken转为NSString
NSString *deviceStr = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
deviceStr = [deviceStr stringByReplacingOccurrencesOfString:@" " withString:@""];
// 把deviceToken传给我们自己的服务器
}
// 获取deviceToken失败
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(@"error:%@",error.description);
}
3、app接收到远程推送的消息:
#pragma mark iOS10 收到通知(本地和远程)
// App接收通知时
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler API_AVAILABLE(ios(10.0)){
// 收到推送的请求
UNNotificationRequest *request = notification.request;
// 收到推送的内容
UNNotificationContent *content = request.content;
// 收到推送的基本信息
NSDictionary *userInfo = content.userInfo;
// 收到推送消息的角标
NSNumber *badge = content.badge;
// 推送消息的声音
UNNotificationSound *sound = content.sound;
// 推送消息的副标题
NSString *subtitle = content.subtitle;
// 推送消息的标题
NSString *title = content.title;
// 推送消息的类型
if ([request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
// 远程通知
} else if ([request.trigger isKindOfClass:[UNTimeIntervalNotificationTrigger class]]) {
// 本地通知,一定时间之后,重复或者不重复推送通知 我们可以设置timeInterval(时间间隔)和repeats(是否重复)。
} else if ([request.trigger isKindOfClass:[UNCalendarNotificationTrigger class]]) {
// 本地通知,一定日期之后,重复或者不重复推送通知 例如,你每天8点推送一个通知,只要dateComponents为8,如果你想每天8点都推送这个通知,只要repeats为YES就可以了
} else if ([request.trigger isKindOfClass:[UNLocationNotificationTrigger class]]) {
// 本地通知,地理位置的一种通知,当用户进入或离开一个地理区域来通知。
}
// 执行下面方法,选择提醒用户的方式
completionHandler(UNNotificationPresentationOptionBadge|
UNNotificationPresentationOptionSound|
UNNotificationPresentationOptionAlert);
}
// App通知的点击事件,只会是用户点击消息才会触发,如果使用户长按(3DTouch)、Action等并不会触发。
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(ios(10.0)){
// 收到推送的请求
UNNotificationRequest *request = response.notification.request;
// 。。。
// 系统要求执行这个方法
completionHandler();
}
#pragma mark iOS10 之前收到通知
// iOS10以下收到本地推送通知
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
}
// iOS7及以上收到通知
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
completionHandler(UIBackgroundFetchResultNewData);
}
// iOS6及以下收到通知
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
}
三、本地推送:
本地推送这里主要分两个版本来介绍,一个是iOS 10及以后,一个是iOS 10以前;
1、iOS 10及以上本地推送生成流程:
创建一个触发器 trigger
创建推送的内容 UNMutableNotificationContent
创建推送请求 UNNotificationRequest
-
推送请求添加到推送管理中心 UNUserNotificationCenter
- (void)postLocalNotificatonUpper10 API_AVAILABLE(ios(10.0)) { // 1. 创建一个触发器(trigger),这里以 UNTimeIntervalNotificationTrigger 为例 UNTimeIntervalNotificationTrigger *timeTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:30 repeats:NO]; // 2. 创建推送的内容 UNMutableNotificationContent UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; content.title = @"title"; content.subtitle = @"subtitle"; content.body = @"body"; content.badge = @2; content.sound = [UNNotificationSound defaultSound]; content.userInfo = @{@"key1" : @"value1", @"key2" : @"value2"}; // // 推送交互操作 // content.categoryIdentifier = @"Dely_locationCategory"; // [self addNotificationAction]; // 3. 创建推送请求 UNNotificationRequest UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"identifier" content:content trigger:timeTrigger]; // 4. 推送请求添加到推送管理中心 UNUserNotificationCenter UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { if (!error) { NSLog(@"推送已添加成功"); } }]; }
2、iOS 10以下本地推送生成流程:
- 创建本地通知对象 UILocalNotification
- 把本地通知对象加入到app日程表中
- (void)postLocalNotificatonBelow10 {
/*
fireDate:启动时间
timeZone:启动时间参考的时区
repeatInterval:重复推送时间(NSCalendarUnit类型),0代表不重复
repeatCalendar:重复推送时间(NSCalendar类型)
alertBody:通知内容
alertAction:解锁滑动时的事件
alertLaunchImage:启动图片,设置此字段点击通知时会显示该图片
alertTitle:通知标题,适用iOS8.2之后
applicationIconBadgeNumber:收到通知时App icon的角标
soundName:推送是带的声音提醒,设置默认的字段为UILocalNotificationDefaultSoundName
userInfo:发送通知时附加的内容
category:此属性和注册通知类型时有关联,(有兴趣的同学自己了解,不详细叙述)适用iOS8.0之后
region:带有定位的推送相关属性,具体使用见下面【带有定位的本地推送】适用iOS8.0之后
regionTriggersOnce:带有定位的推送相关属性,具体使用见下面【带有定位的本地推送】适用iOS8.0之后
*/
// 1. 创建本地通知对象 UILocalNotification
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:30];
localNotification.alertBody = @"通知显示内容";
localNotification.alertAction = @"解锁滑动是的事件";
localNotification.applicationIconBadgeNumber = 1;
localNotification.soundName = UILocalNotificationDefaultSoundName;
// 2. 把本地通知对象加入到app日程表中
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
// 立即发送通知
// [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
}
四、iOS 10推送交互操作:
iOS 10中,可以允许推送添加交互操作 action,这些 action 使得app可以在前台或者后台执行一些逻辑代码。这是推送功能的一个拓展,可通过3D-Touch触发,或者右滑会出现view和clear选项来触发。
- 创建action
- 创建category
- 把category添加到通知中心
// 添加推送交互操作
- (void)addNotificationAction API_AVAILABLE(ios(10.0)) {
/*
1.需要解锁显示,点击不会进app
UNNotificationActionOptionAuthenticationRequired
2.点击不会进app
UNNotificationActionOptionDestructive
3.点击会进app
UNNotificationActionOptionForeground
*/
// 1. 创建action
UNNotificationAction *lookAction =
[UNNotificationAction actionWithIdentifier:@"action.join"
title:@"接收邀请"
options:UNNotificationActionOptionAuthenticationRequired];
UNNotificationAction *joinAction =
[UNNotificationAction actionWithIdentifier:@"action.look"
title:@"查看邀请"
options:UNNotificationActionOptionForeground];
UNNotificationAction *cancelAction =
[UNNotificationAction actionWithIdentifier:@"action.cancel"
title:@"取消"
options:UNNotificationActionOptionDestructive];
// 2. 创建category
// * identifier 标识符
// * actions 操作数组
// * intentIdentifiers 意图标识符 可在 <Intents/INIntentIdentifiers.h> 中查看,主要是针对电话、carplay 等开放的 API。
// * options 通知选项 枚举类型 也是为了支持 carplay
UNNotificationCategory *category =
[UNNotificationCategory categoryWithIdentifier:@"Dely_locationCategory"
actions:@[lookAction, joinAction, cancelAction]
intentIdentifiers:@[]
options:UNNotificationCategoryOptionCustomDismissAction];
// 3. 把category添加到通知中心
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObject:category]];
}
// 添加推送文本输入交互操作
- (void)addTextNotificationAction API_AVAILABLE(ios(10.0)) {
// 创建 UNTextInputNotificationAction 比 UNNotificationAction 多了两个参数
// * buttonTitle 输入框右边的按钮标题
// * placeholder 输入框占位符
UNTextInputNotificationAction *inputAction =
[UNTextInputNotificationAction actionWithIdentifier:@"action.input"
title:@"输入"
options:UNNotificationActionOptionForeground
textInputButtonTitle:@"发送"
textInputPlaceholder:@"tell me loudly"];
// 注册 category
UNNotificationCategory *notificationCategory =
[UNNotificationCategory categoryWithIdentifier:@"Dely_locationCategory"
actions:@[inputAction]
intentIdentifiers:@[]
options:UNNotificationCategoryOptionCustomDismissAction];
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObject:notificationCategory]];
}
- 事件的操作
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(ios(10.0)){
// iOS 10推送交互事件的操作,在这里处理
//点击或输入action
NSString* actionIdentifierStr = response.actionIdentifier;
//输入
if ([response isKindOfClass:[UNTextInputNotificationResponse class]]) {
NSString* userSayStr = [(UNTextInputNotificationResponse *)response userText];
}
//点击
if ([actionIdentifierStr isEqualToString:@"action.join"]) {
} else if ([actionIdentifierStr isEqualToString:@"action.look"]) {
}
// 系统要求执行这个方法
completionHandler();
}
注意,远程推送一定要保证 category 的键值对是一致的
{
"aps" : {
"alert" : {
"title" : "iOS远程消息,我是主标题!-title",
"subtitle" : "iOS远程消息,我是主标题!-Subtitle",
"body" : "Dely,why am i so handsome -body"
},
"category" : "Dely_locationCategory",
"badge" : "2"
}
}