原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容
目录
- 一、简介
- 一、Local Notifications(本地推送)
- 1、本地推送流程
- 2、通知的触发条件
- 3、通知的内容
- 4、对推送进行查、改、删
- 5、UNUserNotificationCenterDelegate中的回调方法
- 二、Remote Notifications(远程推送)
- 1、远程推送流程
- 2、准备工作
- 3、AppDelegate中的方法
- 4、App Server
- 三、iOS 通知扩展
- 1、准备工作
- 2、通知服务扩展(UNNotificationServiceExtension)
- 3、通知内容扩展(UNNotificationContentExtension)
- 四、极光推送
- 1、简介
- 2、项目中极光SDK的配置
- 3、JPUSHRegisterDelegate
- 4、封装的便利方法
- Demo
- 参考文献
一、简介
日常生活中会有很多种情形需要通知,比如:新闻提醒、定时吃药、定期体检、到达某个地方提醒用户等等,这些功能在 UserNotifications 中都提供了相应的接口。
iOS推送分为Local Notifications(本地推送) 和 Remote Notifications(远程推送)。本地推送是App本地创建通知,加入到系统的Schedule里,如果触发器条件达成时会推送相应的消息内容。
在远程推送中,Provider是指某个APP的Push服务器。APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器。APNS Pusher应用程序把要发送的消息、目标iPhone的标识(deviceToken)打包,发给APNS。APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发到iPhone。iPhone把发来的消息传递给相应的应用程序, 并且按照设定弹出Push通知。
如果你的App有远端推送的话,那你需要用开发者账号新建一个push证书。再在Capabilities中打开Push Notifications 开关,打开后会自动在项目里生成entitlements文件。
二、Local Notifications(本地推送)
1、本地推送流程
本地推送通知是由本地应用触发的,是基于时间的通知形式,一般用于闹钟定时、待办事项等提醒功能。
a、发送本地推送通知的步骤
- 创建一个触发器(
trigger) - 创建推送的内容(
UNMutableNotificationContent) - 创建推送请求(
UNNotificationRequest) - 推送请求添加到推送管理中心(
UNUserNotificationCenter)中
b、步骤的代码实现
- (void)simpleLocalNotificationDescribe
{
[self buildNotificationDescribe:@"最简单的本地通知(创建5秒后触发,建议回到桌面察看效果)"];
}
- (void)simpleLocalPushService
{
// 1.定时推送
UNTimeIntervalNotificationTrigger *trigger = [self getTimeTrigger];
// 2.推送的内容
UNMutableNotificationContent *content = [self getSimpleContent];
// 3.创建通知请求 UNNotificationRequest 将触发条件和通知内容添加到请求中
NSString *requestIdentifer = @"Simple Local Notification";
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:requestIdentifer content:content trigger:trigger];
// 4.将通知请求 add 到 UNUserNotificationCenter
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (!error)
{
NSLog(@"简单的本地通知已添加成功!");
// 此处省略一万行需求.......
}
}];
}
2、通知的触发条件
苹果把本地通知跟远程通知合二为一。区分本地通知跟远程通知的类是UNNotificationTrigger,通过它,我们可以得到一些通知的触发条件。
UNPushNotificationTrigger// 远程推送的通知类型
UNTimeIntervalNotificationTrigger// (本地通知) 一定时间之后,重复或者不重复推送通知。我们可以设置timeInterval(时间间隔)和repeats(是否重复)
UNCalendarNotificationTrigger//(本地通知) 一定日期之后,重复或者不重复推送通知 例如,你每天8点推送一个通知,只要dateComponents为8,如果你想每天8点都推送这个通知,只要repeats为YES就可以了
UNLocationNotificationTrigger// (本地通知)地理位置的一种通知,当用户进入或离开一个地理区域来通知
定时推送
// 触发推送的时机。timeInterval:单位为秒(s) repeats:是否循环提醒
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5 repeats:NO];
定期推送
// components代表日期,这里指在每周一的14点3分提醒
NSDateComponents *components = [[NSDateComponents alloc] init];
components.weekday = 2;
components.hour = 14;
components.minute = 3;
UNCalendarNotificationTrigger *calendarTrigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:YES];
定点推送
// 使用CLRegion的子类CLCircularRegion创建位置信息
CLLocationCoordinate2D center1 = CLLocationCoordinate2DMake(39.788857, 116.5559392);
CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center1 radius:500 identifier:@"海峡国际社区"];
// 进入地区、从地区出来或者两者都要的时候进行通知
region.notifyOnEntry = YES;
region.notifyOnExit = YES;
// region 位置信息 repeats 是否重复
UNLocationNotificationTrigger *locationTrigger = [UNLocationNotificationTrigger triggerWithRegion:region repeats:YES];
3、通知的内容
a、文字、图像、声音


- (UNMutableNotificationContent *)getSimpleContent
{
// 推送的文本内容
// UNNotificationContent的属性readOnly,而UNMutableNotificationContent的属性可以更改
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
// 限制在一行,多出部分省略号
content.title = @"时间提醒";
content.subtitle = [NSString stringWithFormat:@"《请回答1988》第二季放映的时间提醒"];
// body中printf风格的转义字符,比如说要包含%,需要写成%% 才会显示,\同样
content.body = @"口若悬河、妙语迭出的精彩表演,从作者津津乐道的口吻可以看出,王尔德无疑是在顾影自怜,因为他自己正是这样的作秀高手,而他在社交圈中越练越“酷”的口才,在他的社会喜剧中得到了淋漓尽致的发挥,令观众如醉如痴!";
content.badge = @5;
// UNNotificationSound *customSound = [UNNotificationSound soundNamed:@""];// 自定义声音
content.sound = [UNNotificationSound defaultSound];
content.userInfo = @{@"useName":@"XieJiapei",@"age":@"22"};
// 辅助图像,下拉通知会放大图像
NSString *imageFilePath = [[NSBundle mainBundle] pathForResource:@"luckcoffee" ofType:@"JPG"];
if (imageFilePath)
{
NSError* error = nil;
UNNotificationAttachment *imageAttachment = [UNNotificationAttachment attachmentWithIdentifier:@"imageAttachment" URL:[NSURL fileURLWithPath:imageFilePath] options:nil error:&error];
if (imageAttachment)
{
// 这里设置的是Array,但是只会取lastObject
content.attachments = @[imageAttachment];
}
}
return content;
}
b、视频

- (UNMutableNotificationContent *)getVideoContent
{
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
content.title = @"WWDC";
content.subtitle = @"苹果开发者技术大会";
content.body = @"下拉通知可直接播放";
// 导入视频的时候,默认不是添加到bundle中,必须手动勾选Add to targets
NSString *videoFilePath = [[NSBundle mainBundle] pathForResource:@"notification_video" ofType:@"m4v"];
if (videoFilePath)
{
UNNotificationAttachment* videoAttachment = [UNNotificationAttachment attachmentWithIdentifier:@"videoAttachment" URL:[NSURL fileURLWithPath:videoFilePath] options:nil error:nil];
if (videoAttachment)
{
// 这里设置的是Array,但是只会取lastObject
content.attachments = @[videoAttachment];
}
}
return content;
}
c、操作


- (UNMutableNotificationContent *)getActionContent
{
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
content.title = @"Apple";
content.subtitle = @"Apple Developer";
content.body = @"下拉放大图片";
NSMutableArray *actionMutableArray = [[NSMutableArray alloc] initWithCapacity:3];
// UNNotificationActionOptionAuthenticationRequired 需要解锁显示,点击不会进app
UNNotificationAction *unUnlockAction = [UNNotificationAction actionWithIdentifier:@"IdentifierNeedUnUnlock" title:@"需要解锁" options: UNNotificationActionOptionAuthenticationRequired];
// UNNotificationActionOptionDestructive 红色文字,点击不会进app
UNNotificationAction *destructiveAction = [UNNotificationAction actionWithIdentifier:@"IdentifierRed" title:@"红色显示" options: UNNotificationActionOptionDestructive];
// UNNotificationActionOptionForeground 黑色文字,点击会进app
// UNTextInputNotificationAction是输入框Action,buttonTitle是输入框右边的按钮标题,placeholder是输入框占位符
UNTextInputNotificationAction *inputTextAction = [UNTextInputNotificationAction actionWithIdentifier:@"IdentifierInputText" title:@"输入文本" options:UNNotificationActionOptionForeground textInputButtonTitle:@"发送" textInputPlaceholder:@"说说今天发生了啥......"];
[actionMutableArray addObjectsFromArray:@[unUnlockAction, destructiveAction, inputTextAction]];
if (actionMutableArray.count > 1)
{
/**categoryWithIdentifier方法
* identifier:是这个category的唯一标识,用来区分多个category,这个id不管是Local Notification,还是remote Notification,一定要有并且要保持一致
* actions:创建action的操作数组
* intentIdentifiers:意图标识符 可在 <Intents/INIntentIdentifiers.h> 中查看,主要是针对电话、carplay 等开放的 API
* options:通知选项 枚举类型 也是为了支持 carplay
*/
UNNotificationCategory *categoryNotification = [UNNotificationCategory categoryWithIdentifier:@"categoryOperationAction" actions:actionMutableArray intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];
// 将创建的 category 添加到通知中心
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObject:categoryNotification]];
// category的唯一标识,Local Notification保持一致
content.categoryIdentifier = @"categoryOperationAction";
}
return content;
}
4、对推送进行查、改、删
a、更新通知
Local Notification重新创建具有相同requestIdentifier的local Notification request添加到推送center就可以了。Remote Notification 更新需要通过新的字段apps-collapse-id来作为唯一标示,APNS pusher暂不支持这个字段,不过github上有这样的工具:Knuff。
b、查找和删除通知
//获取未送达的所有消息列表
- (void)getPendingNotificationRequestsWithCompletionHandler:(void(^)(NSArray<UNNotificationRequest *> *requests))completionHandler;
//删除所有未送达的特定id的消息
- (void)removePendingNotificationRequestsWithIdentifiers:(NSArray<NSString *> *)identifiers;
//删除所有未送达的消息
- (void)removeAllPendingNotificationRequests;
//获取已送达的所有消息列表
- (void)getDeliveredNotificationsWithCompletionHandler:(void(^)(NSArray<UNNotification *> *notifications))completionHandler;
//删除所有已送达的特定id的消息
- (void)removeDeliveredNotificationsWithIdentifiers:(NSArray<NSString *> *)identifiers;
//删除所有已送达的消息
- (void)removeAllDeliveredNotifications;
调用方式如下:
- (void)removeNotificaiton
{
NSString *requestIdentifier = @"XieJiaPei";
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
// 删除设备已收到特定id的所有消息推送
[center removeDeliveredNotificationsWithIdentifiers:@[requestIdentifier]];
// 删除设备已收到的所有消息推送
[center removeAllDeliveredNotifications];
// 获取设备已收到的消息推送
[center getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
NSLog(@"获取设备已收到的消息推送");
}];
}
5、UNUserNotificationCenterDelegate中的回调方法
a、即将展示推送的通知时触发(app在前台获取到通知)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
{
// 收到推送的请求
UNNotificationRequest *request = notification.request;
// 收到的内容
UNNotificationContent *content = request.content;
// 收到用户的基本信息
NSDictionary *userInfo = content.userInfo;
// 收到消息的角标
NSNumber *badge = content.badge;
// 收到消息的body
NSString *body = content.body;
// 收到消息的声音
UNNotificationSound *sound = content.sound;
// 推送消息的副标题
NSString *subtitle = content.subtitle;
// 推送消息的标题
NSString *title = content.title;
if ([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]])// 远程推送的通知
{
NSLog(@"远程推送的通知,收到用户的基本信息为: %@\n",userInfo);
}
else // 本地通知
{
NSLog(@"本地推送的通知:{\nbody:%@,\ntitle:%@,\nsubtitle:%@,\nbadge:%@,\nsound:%@,\nuserInfo:%@}",body,title,subtitle,badge,sound,userInfo);
}
// 不管前台后台状态下。推送消息的横幅都可以展示出来
// 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Banner三种类型可以设置
completionHandler(UNNotificationPresentationOptionBadge|
UNNotificationPresentationOptionSound|
UNNotificationPresentationOptionBanner);
}
本地通知的输出结果
2020-10-26 16:08:53.383283+0800 PushServiceDemo[71205:2358029] 简单的本地通知已添加成功!
2020-10-26 16:08:58.393934+0800 PushServiceDemo[71205:2357722] 本地推送的通知:{
body:口若悬河、妙语迭出的精彩表演,从作者津津乐道的口吻可以看出,王尔德无疑是在顾影自怜,因为他自己正是这样的作秀高手,而他在社交圈中越练越“酷”的口才,在他的社会喜剧中得到了淋漓尽致的发挥,令观众如醉如痴!,
title:时间提醒,
subtitle:《请回答1988》第二季放映的时间提醒,
badge:5,
sound:<UNNotificationSound: 0x6000038f1a40>,
userInfo:{
age = 22;
useName = XieJiapei;
}}
b、用户点击推送消息时触发 (点击通知进入app时触发)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
{
// UNNotificationResponse 是普通按钮的Response
NSString *actionIdentifierString = response.actionIdentifier;
if (actionIdentifierString)
{
// 点击后展示文本2秒后隐藏
UIView *windowView = [[[UIApplication sharedApplication] keyWindow] rootViewController].view;
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:windowView animated:YES];
hud.label.text = [NSString stringWithFormat:@"用户点击了消息,id为:%@",actionIdentifierString];
hud.mode = MBProgressHUDModeText;
[hud hideAnimated:YES afterDelay:2];
if ([actionIdentifierString isEqualToString:@"IdentifierNeedUnUnlock"])
{
NSLog(@"需要解锁");
}
else if ([actionIdentifierString isEqualToString:@"IdentifierRed"])
{
NSLog(@"红色显示,并且设置APP的Badge通知数字为0");
// 设置APP的Badge通知数字
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
}
}
// UNTextInputNotificationResponse 是带文本输入框按钮的Response
if ([response isKindOfClass:[UNTextInputNotificationResponse class]])
{
NSString *userSayString = [(UNTextInputNotificationResponse *)response userText];
if (userSayString)
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIView *windowView = [[[UIApplication sharedApplication] keyWindow] rootViewController].view;
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:windowView animated:YES];
hud.label.text = userSayString;
hud.mode = MBProgressHUDModeText;
[hud hideAnimated:YES afterDelay:2];
});
}
}
// 系统要求执行这个方法
completionHandler();
}
点击红色按钮的输出结果为:
2020-10-27 16:20:02.797425+0800 PushServiceDemo[80030:2808431] 红色显示,并且设置APP的Badge通知数字为0
二、Remote Notifications(远程推送)
1、远程推送流程
远程推送通知是通过苹果的APNs(Apple Push Notification service)发送到app,而APNs必须先知道用户设备的令牌(device token)。在启动时,app与APNs通信并接收device token,然后将其转发到App Server,App Server将该令牌和要发送的通知消息发送至APNs。
苹果官方提供的远程推送通知的传递示意图如下:

各关键组件之间的交互细节:

2、准备工作
- 根据工程的
Bundle Identifier,在苹果开发者平台中创建同名App ID,并勾选Push Notifications服务 - 在工程的
Capabilities中启动Push Notifications - 远程推送必须使用真机调试,因为模拟器无法获取得到
device token
想要为苹果开发软件并上架苹果商店,就需要参加苹果开发者计划,需要交纳年费(699两档),只要求自己编写的代码在苹果真机上跑起来,只需要注册成苹果开发者账户就可以了,不需要交钱,但是如果想调试推送、iCloud、IAP之类的功能,或者上架苹果商店,就需要交钱了。买不起~~~~无法调试呀🧐以后有开发者账户了再看看吧~生成APNs后端推送证书
3、AppDelegate中的方法
a、注册远程通知
- (void)registerPushService
{
// 远程通知授权
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted)
{
NSLog(@"远程通知中心成功打开");
// 必须在主线程注册通知
dispatch_async(dispatch_get_main_queue(), ^{
// 注册远程通知
[[UIApplication sharedApplication] registerForRemoteNotifications];
// 注册delegate
[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];
});
}
else
{
NSLog(@"远程通知中心打开失败");
}
}];
// 获取注册之后的权限设置
// 注意UNNotificationSettings是只读对象,不能直接修改
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
NSLog(@"通知的配置信息:\n%@",settings);
}];
}
输出结果为:
2020-10-26 16:00:00.694842+0800 PushServiceDemo[71205:2357947] 通知的配置信息:
<UNNotificationSettings: 0x6000038f0d20; authorizationStatus: Authorized, notificationCenterSetting: Enabled, soundSetting: Enabled, badgeSetting: Enabled, lockScreenSetting: Enabled, carPlaySetting: NotSupported, announcementSetting: NotSupported, criticalAlertSetting: NotSupported, alertSetting: Enabled, alertStyle: Banner, groupingSetting: Default providesAppNotificationSettings: No>
2020-10-26 16:00:00.696243+0800 PushServiceDemo[71205:2357946] 远程通知中心成功打开
b、App获取device token
app将获取到的device token发送给App Server。只有苹果公司知道device token的生成算法,保证唯一。device token在app卸载后重装等情况时会变化,因此为确保device token变化后app仍然能够正常接收服务器端发送的通知,建议每次启动应用都将获取到的device token传给App Server。
// 远端推送需要获取设备的Device Token
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
// 解析NSData获取字符串
NSString *deviceString = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];// 移除<>
deviceString = [deviceString stringByReplacingOccurrencesOfString:@" " withString:@""];// 移除空格
NSLog(@"设备的Device Token为:%@",deviceString);
}
// 获取设备的DeviceToken失败
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
NSLog(@"获取设备的DeviceToken失败:%@\n",error.description);
}
4、App Server
Pusher的下载地址
NWPusher可以当做framework使用,也可以直接下载APP使用。
Pusher的使用步骤
使用Pusher工具模拟App Server将指定的device token和消息内容发送给APNs。

- 选择p12格式的推送证书
- 设置是否为测试环境。默认勾选为测试环境,由于推送证书分为测试证书和生产证书,并且苹果的
APNs也分为测试和生产两套环境,因此Pusher需要手动勾选推送环境 - 输入
device token - 输入符合苹果要求格式的
aps字符串 - 执行推送
内容格式
在Payload中输入的内容就是我们需要传送的数据了,这个数据传输以JSON的格式存储。
{"aps":{"alert":{"title":"通知的title","subtitle":"通知的subtitle","body":"通知的body","title-loc-key":"TITLE_LOC_KEY","title-loc-args":["t_01","t_02"],"loc-key":"LOC_KEY","loc-args":["l_01","l_02"]},"sound":"sound01.wav","badge":1,"mutable-content":1,"category": "realtime"},"msgid":"123"}
- aps:我们需要传送的内容
- alert:弹出框需要展示的内容
- badge:展示的信息个数
-
sound:表示当有
Push消息的时候,是否需要声音提示
稍纵即逝你就收到了远端消息了
可惜没开发者账户无法测试,连device token都拿不到。
2020-10-27 15:06:34.967968+0800 PushServiceDemo[1272:220269] 获取设备的DeviceToken失败:Error Domain=NSCocoaErrorDomain Code=3000 "未找到应用程序的“aps-environment”的授权字符串" UserInfo={NSLocalizedDescription=未找到应用程序的“aps-environment”的授权字符串}
三、iOS 通知扩展
1、准备工作
a、添加新的Target--> Notification Service/Content

b、扩展工程的目录
系统会自动创建一个 UNNotificationServiceExtension 的子类 NotificationService。通过完善这个子类,来实现你的需求。NotificationViewController直接继承于ViewController,因此可以在这个类中重写相关方法,来修改界面的相关布局及样式。

c、扩展提供的方法
Notification Service
让你可以在后台处理接收到的推送,传递最终的内容给 contentHandler。系统接到通知后,有最多30秒在这里重写通知内容(如下载附件并更新通知)。
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler
{
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];
self.contentHandler(self.bestAttemptContent);
}
在你获得的一小段运行通知代码的时间即将结束的时候,如果仍然没有成功的传入内容,会走到这个方法,可以在这里传肯定不会出错的内容,或者默认传递原始的推送内容。处理过程超时,则收到的通知直接展示出来。
- (void)serviceExtensionTimeWillExpire
{
// 将获取到的内容传递给content扩展
self.contentHandler(self.bestAttemptContent);
}
Notification Content
在这儿做界面初始化的工作。
- (void)viewDidLoad
{
[super viewDidLoad];
}
获取通知信息,更新UI控件中的数据。
- (void)didReceiveNotification:(UNNotification *)notification
{
self.label.text = notification.request.content.body;
self.titleLabelA.text = [NSString stringWithFormat:@"%@ + %@", notification.request.content.title, notification.request.content.subtitle];
self.titleLabelB.text = [NSString stringWithFormat:@" = %ld", [notification.request.content.title integerValue] + [notification.request.content.subtitle integerValue]];
}
d、调整 info.plist
使用自定义的NotificationContent的时候,需要对应extension中info.plist,因为推送通知内容中的category字段,与UNNotificationContentExtension的info.plist中UNNotificationExtensionCategory字段的值要匹配,系统才能找到自定义的UI。
categoryIdentifier
UNNotificationExtensionCategory默认是string类型,可以手动更改成array类型,array中的item(string)是categoryName。在收到通知的时候,我们可以让服务器把这个通知的categoryIdentifier带上,作用是我们可以根据视频、音乐、图片来分别自定义我们的通知内容。不同的分类标识符,也会在使用UNNotificationAction的时候帮助我们区分是什么类型的通知,方便我们对不同类型的通知做出不同的操作行为。我们目前在Service、Content、aps写死了categoryIdentifier,其实在收到系统推送时,每一个推送内容最好带上一个跟服务器约定好了的categoryIdentifier,这样方便我们根据categoryIdentifier来自定义不同类型的视图,以及action。
UNNotificationExtensionInitialContentSizeRatio
UNNotificationExtensionInitialContentSizeRatio 这个值必须要有,类型是一个浮点类型,代表的是高度与宽度的比值。系统会使用这个比值,作为初始化view的大小。举个简单的例子来说,如果该值为1,则该视图为正方形。如果为0.5,则代表高度是宽度的一半。注意这个值只是初始化的一个值,在这个扩展添加后,可以重写frame,展示的时候,在我们还没打开这个视图预览时,背景是个类似图片占位的灰色,那个灰色的高度宽度之比,就是通过这个值来设定。
UNNotificationExtensionDefaultContentHidden
UNNotificationExtensionDefaultContentHidden 这个值可选,是一个BOOL值。当为YES时,会隐藏上方原本推送的内容视图,只会显示我们自定义的视图,因为在自定义视图的时候,我们可以取得推送内容,然后按照我们想要的布局,展示出来。如果为NO时(默认为NO),推送视图就会既有我们的自定义视图,也会有系统原本的推送内容视图(这里附件是不会显示的,只会显示body里面的文字哟)。这里需要隐藏默认消息框,所以添加UNNotificationExtensionDefaultContentHidden属性,Bool(YES)。
NSExtensionMainStoryboard
至于NSExtensionMainStoryboard以及NSExtensionPointIdentifier,系统默认生成,大家直接用就好,如果需要更改的,只能更改使用的storyboard的名字(不过应该没人会把系统的删除再建立一个吧 O(∩_∩)O)
最初的info.plist

修改成array后的info.plist

修改后Service和aps的info.plist

2、通知服务扩展(UNNotificationServiceExtension)
a、简介
支持附带 Media Attachments
本地推送和远程推送同时都可支持附带Media Attachments。不过远程通知需要实现通知服务扩展UNNotificationServiceExtension,在service extension里面去下载attachment,但是需要注意,service extension会限制下载的时间(30s),并且下载的文件大小也会同样被限制。这里毕竟是一个推送,而不是把所有的内容都推送给用户。所以你应该去推送一些缩小比例之后的版本。比如图片,推送里面附带缩略图,当用户打开app之后,再去下载完整的高清图。视频就附带视频的关键帧或者开头的几秒,当用户打开app之后再去下载完整视频。
UNNotificationAttachment 支持的附件格式和大小限制
- 音频5M(
kUTTypeWaveformAudio/kUTTypeMP3/kUTTypeMPEG4Audio/kUTTypeAudioInterchangeFileFormat) - 图片10M(
kUTTypeJPEG/kUTTypeGIF/kUTTypePNG) - 视频50M(
kUTTypeMPEG/kUTTypeMPEG2Video/kUTTypeMPEG4/kUTTypeAVIMovie)
校验附件
系统会在通知注册前校验附件,如果附件出问题,通知注册失败;校验成功后,附件会转入attachment data store;如果附件是在app bundle,则是会被copy来取代move。attachment data store的位置?利用代码测试获取在磁盘上的图片文件作为attachment,会发现注册完通知后,图片文件被移除,在app的沙盒中找不到该文件在哪里。
b、NotificationService文件
#import <UserNotifications/UserNotifications.h>
@interface NotificationService : UNNotificationServiceExtension
@end
@interface NotificationService ()
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@end
@implementation NotificationService
@end
c、Demo演示
最多30秒重写通知内容
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler
{
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
// 修改通知的内容
self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [ServiceExtension modified]", self.bestAttemptContent.title];
// 设置UNNotificationAction
[self getAction];
// category的唯一标识,Remote Notification保持一致
self.bestAttemptContent.categoryIdentifier = @"categoryOperationAction";
// 加载网络请求
NSDictionary *userInfo = self.bestAttemptContent.userInfo;
NSString *mediaUrl = userInfo[@"media"][@"url"];
NSString *mediaType = userInfo[@"media"][@"type"];
if (!mediaUrl.length)// 不存在url则使用基本的内容
{
self.contentHandler(self.bestAttemptContent);
}
else// 否则使用网络请求到的内容
{
// 创建附件资源
// UNNotificationAttachment的url接收的是本地文件的url
// 附件资源必须存在本地,如果是远程推送的网络资源需要提前下载到本地
[self loadAttachmentForUrlString:mediaUrl withType:mediaType completionHandle:^(UNNotificationAttachment *attach) {
if (attach)
{
// 将附件资源添加到 UNMutableNotificationContent 中
self.bestAttemptContent.attachments = [NSArray arrayWithObject:attach];
}
self.contentHandler(self.bestAttemptContent);
}];
}
}
将获取到的内容传递给content扩展
- (void)serviceExtensionTimeWillExpire
{
self.contentHandler(self.bestAttemptContent);
}
网络请求
- (void)loadAttachmentForUrlString:(NSString *)urlStr withType:(NSString *)type completionHandle:(void(^)(UNNotificationAttachment *attach))completionHandler
{
__block UNNotificationAttachment *attachment = nil;
// 附件的URL
NSURL *attachmentURL = [NSURL URLWithString:urlStr];
// 获取媒体类型的后缀
NSString *fileExt = [self getfileExtWithMediaType:type];
// 从网络下载媒体资源
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session downloadTaskWithURL:attachmentURL completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) {
if (error)
{
NSLog(@"加载多媒体失败 %@", error.localizedDescription);
}
else
{
// 将下载好的媒体文件拷贝到目的路径
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *localURL = [NSURL fileURLWithPath:[temporaryFileLocation.path stringByAppendingString:fileExt]];
[fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error];
// 自定义推送UI需要
NSMutableDictionary *dict = [self.bestAttemptContent.userInfo mutableCopy];
// 这里使用图片测试
[dict setObject:[NSData dataWithContentsOfURL:localURL] forKey:@"image"];
self.bestAttemptContent.userInfo = dict;
NSError *attachmentError = nil;
// category的唯一标识,Remote Notification保持一致
// URL 资源路径
// options 资源可选操作 比如隐藏缩略图之类的
attachment = [UNNotificationAttachment attachmentWithIdentifier:@"categoryOperationAction" URL:localURL options:nil error:&attachmentError];
if (attachmentError)
{
NSLog(@"%@", attachmentError.localizedDescription);
}
}
// 将附件传递出去
completionHandler(attachment);
}] resume];
}
用于将媒体类型的后缀添加到文件路径上
// 服务端在处理推送内容时,最好加上媒体类型字段
- (NSString *)getfileExtWithMediaType:(NSString *)mediaType
{
NSString *fileExt = mediaType;
if ([mediaType isEqualToString:@"image"])
{
fileExt = @"jpg";
}
if ([mediaType isEqualToString:@"video"])
{
fileExt = @"mp4";
}
if ([mediaType isEqualToString:@"audio"])
{
fileExt = @"mp3";
}
return [@"." stringByAppendingString:fileExt];
}
用户操作
- (void)getAction
{
NSMutableArray *actionMutableArray = [[NSMutableArray alloc] initWithCapacity:3];
// UNNotificationActionOptionAuthenticationRequired 需要解锁显示,点击不会进app
UNNotificationAction *unUnlockAction = [UNNotificationAction actionWithIdentifier:@"IdentifierNeedUnUnlock" title:@"需要解锁" options: UNNotificationActionOptionAuthenticationRequired];
// UNNotificationActionOptionDestructive 红色文字,点击不会进app
UNNotificationAction *destructiveAction = [UNNotificationAction actionWithIdentifier:@"IdentifierRed" title:@"红色显示" options: UNNotificationActionOptionDestructive];
// UNNotificationActionOptionForeground 黑色文字,点击会进app
// UNTextInputNotificationAction是输入框Action,buttonTitle是输入框右边的按钮标题,placeholder是输入框占位符
UNTextInputNotificationAction *inputTextAction = [UNTextInputNotificationAction actionWithIdentifier:@"IdentifierInputText" title:@"输入文本" options:UNNotificationActionOptionForeground textInputButtonTitle:@"发送" textInputPlaceholder:@"说说今天发生了啥......"];
NSArray *identifierArray = [[NSArray alloc] initWithObjects:@"IdentifierNeedUnUnlock", @"IdentifierRed", @"IdentifierInputText", nil];
[actionMutableArray addObjectsFromArray:@[unUnlockAction, destructiveAction, inputTextAction]];
if (actionMutableArray.count > 1)
{
/**categoryWithIdentifier方法
* identifier:是这个category的唯一标识,用来区分多个category,这个id不管是Local Notification,还是remote Notification,一定要有并且要保持一致
* actions:创建action的操作数组
* intentIdentifiers:意图标识符 可在 <Intents/INIntentIdentifiers.h> 中查看,主要是针对电话、carplay 等开放的 API
* options:通知选项 枚举类型 也是为了支持 carplay
*/
UNNotificationCategory *categoryNotification = [UNNotificationCategory categoryWithIdentifier:@"categoryOperationAction" actions:actionMutableArray intentIdentifiers:identifierArray options:UNNotificationCategoryOptionCustomDismissAction];
// 将创建的 category 添加到通知中心
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObject:categoryNotification]];
}
}
d、APP Server的aps
mutable-content这个键值为1,这意味着此条推送可以被 Service Extension 进行更改,也就是说要用Service Extension需要加上这个键值为1。
{"aps":{"alert":{"title":"Title...","subtitle":"Subtitle...","body":"Body..."},"sound":"default","badge": 1,"mutable-content": 1,"category": "categoryOperationAction",},"msgid":"123","media":{"type":"image","url":"https://www.fotor.com/images2/features/photo_effects/e_bw.jpg"}}
e、特别说明
❶ Notification Service Extension在使用时需要配置相关证书,我没有开发者账号,所以无法调试。🙂(买不起,贫穷限制了我的开发能力......)
❷ 要选择相应的target来运行工程。

❸ 加断点调试怎么不走相应方法?一个朋友找了很长时间的原因发现是xcode的问题,那个朋友就是我......
3、通知内容扩展(UNNotificationContentExtension)
a、在展示通知时展示一个自定义的用户界面。
这个就是个简单的storyboard文件,内部有一个View,这个View就是在上面的图层中的自定义View视图了。它与NotificationViewController所绑定。

这里使用纯代码方式来创建界面,所以需要删除MainInterface文件,然后在Notifications Content 的info.plist中把NSExtensionMainStoryboard替换为NSExtensionPrincipalClass,并且value对应我们的类名NotificationViewController。
#define Margin 15
@interface NotificationViewController () <UNNotificationContentExtension>
@property (nonatomic, strong) UILabel *label;
@property (nonatomic, strong) UILabel *subLabel;
@property (nonatomic, strong) UILabel *hintLabel;
@property (nonatomic, strong) UIImageView *imageView;
@end
- (void)viewDidLoad
{
[super viewDidLoad];
CGPoint origin = self.view.frame.origin;
CGSize size = self.view.frame.size;
self.label = [[UILabel alloc] initWithFrame:CGRectMake(Margin, Margin, size.width-Margin*2, 30)];
self.label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self.view addSubview:self.label];
self.subLabel = [[UILabel alloc] initWithFrame:CGRectMake(Margin, CGRectGetMaxY(self.label.frame)+10, size.width-Margin*2, 30)];
self.subLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self.view addSubview:self.subLabel];
self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(Margin, CGRectGetMaxY(self.subLabel.frame)+10, 100, 100)];
[self.view addSubview:self.imageView];
self.hintLabel = [[UILabel alloc] initWithFrame:CGRectMake(Margin, CGRectGetMaxY(self.imageView.frame)+10, size.width-Margin*2, 20)];
[self.hintLabel setText:@"我是hintLabel"];
[self.hintLabel setFont:[UIFont systemFontOfSize:14]];
[self.hintLabel setTextAlignment:NSTextAlignmentLeft];
[self.view addSubview:self.hintLabel];
self.view.frame = CGRectMake(origin.x, origin.y, size.width, CGRectGetMaxY(self.imageView.frame)+Margin);
// 设置控件边框颜色
[self.label.layer setBorderColor:[UIColor redColor].CGColor];
[self.label.layer setBorderWidth:1.0];
[self.subLabel.layer setBorderColor:[UIColor greenColor].CGColor];
[self.subLabel.layer setBorderWidth:1.0];
[self.imageView.layer setBorderWidth:2.0];
[self.imageView.layer setBorderColor:[UIColor blueColor].CGColor];
[self.view.layer setBorderWidth:2.0];
[self.view.layer setBorderColor:[UIColor cyanColor].CGColor];
}
b、Demo演示
接收到通知的内容
// 生成时默认实现了UNNotificationContentExtension协议的方法
- (void)didReceiveNotification:(UNNotification *)notification
{
self.label.text = notification.request.content.title;
self.subLabel.text = [NSString stringWithFormat:@"%@ [ContentExtension modified]", notification.request.content.subtitle];
// 提取附件
UNNotificationAttachment *attachment = notification.request.content.attachments.firstObject;
if ([attachment.URL startAccessingSecurityScopedResource])
{
NSData *imageData = [NSData dataWithContentsOfURL:attachment.URL];
[self.imageView setImage:[UIImage imageWithData:imageData]];
[attachment.URL stopAccessingSecurityScopedResource];
}
}
用户操作
// 点击通知进入app时触发(杀死/切到后台唤起)
- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption))completion
{
[self.hintLabel setText:[NSString stringWithFormat:@"触发了%@", response.actionIdentifier]];
if ([response.actionIdentifier isEqualToString:@"IdentifierNeedUnUnlock"])
{
NSLog(@"点击了解锁");
}
else if([response.actionIdentifier isEqualToString:@"IdentifierRed"])
{
NSLog(@"点击了红色");
}
else if([response.actionIdentifier isEqualToString:@"IdentifierInputText"])
{
UNTextInputNotificationResponse *textInputResponse = (UNTextInputNotificationResponse *)response;
[self.hintLabel setText:[NSString stringWithFormat:@"用户输入的文字是:%@", textInputResponse.userText]];
}
else
{
NSLog(@"啥?");
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 必须设置completion,否则通知不会消失
// UNNotificationContentExtensionResponseOptionDismiss 直接让该通知消失
// UNNotificationContentExtensionResponseOptionDismissAndForwardAction 消失并传递按钮信息给AppDelegate,是否进入App看Att的设置
completion(UNNotificationContentExtensionResponseOptionDismiss);
});
}
c、控制媒体文件的播放
枚举 UNNotificationContentExtensionMediaPlayPauseButtonType
typedef NS_ENUM(NSUInteger, UNNotificationContentExtensionMediaPlayPauseButtonType) {
// 没有播放按钮
UNNotificationContentExtensionMediaPlayPauseButtonTypeNone,
// 有播放按钮,点击播放之后,按钮依旧存在,类似音乐播放的开关
UNNotificationContentExtensionMediaPlayPauseButtonTypeDefault,
// 有播放按钮,点击后,播放按钮消失,再次点击暂停播放后,按钮恢复
UNNotificationContentExtensionMediaPlayPauseButtonTypeOverlay,
}
设置播放按钮的属性
// 设置播放按钮的属性
@property (nonatomic, readonly, assign) UNNotificationContentExtensionMediaPlayPauseButtonType mediaPlayPauseButtonType;
// 设置播放按钮的frame
@property (nonatomic, readonly, assign) CGRect mediaPlayPauseButtonFrame;
// 设置播放按钮的颜色
@property (nonatomic, readonly, copy) UIColor *mediaPlayPauseButtonTintColor;
// 开始跟暂停播放
- (void)mediaPlay;
- (void)mediaPause;
这些属性都是readonly的,所以直接用self.属性去修改肯定是报错的,所以我们能用的就只有get方法了。
根据button的类型,我们可以联想到,如果button没有,这个播放开始暂停的方法也没用了。如果有button,自然我们就有了播放的操作,我们得出了以下结论。一定要重写它的frame来确定他的位置。指定颜色来设置它的显示颜色。设置button的类型让他显示出来。
// 返回默认样式的button
- (UNNotificationContentExtensionMediaPlayPauseButtonType)mediaPlayPauseButtonType
{
return UNNotificationContentExtensionMediaPlayPauseButtonTypeDefault;
}
// 返回button的frame
- (CGRect)mediaPlayPauseButtonFrame
{
return CGRectMake(100, 100, 100, 100);
}
// 返回button的颜色
- (UIColor *)mediaPlayPauseButtonTintColor
{
return [UIColor blueColor];
}
开始跟暂停播放的方法
// 开始播放
- (void)mediaPlay
{
NSLog(@"mediaPlay,开始播放");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.extensionContext mediaPlayingPaused];
});
}
// 暂停播放
- (void)mediaPause
{
NSLog(@"mediaPause,暂停播放");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.extensionContext mediaPlayingStarted];
});
}
四、极光推送
1、简介
a、极光推送的概念
极光推送(JPush)是独立的第三方云推送平台,致力于为全球移动应用开发者提供移动消息推送服务。极光,是国内领先的移动大数据的服务商。拥有开发者服务、广告服务和数据服务三大产品体系。开发者服务助力app精细运营,覆盖极光推送、极光IM、极光短信、极光统计、社会化分享、极光认证,广告服务,数据服务。说白了,极光推送类似于我们每天在手机上接收到的消息。那我们怎么在自己的app中实现这种功能呢?
b、准备证书和权限
❶ 创建APP ID
APP ID 是每一个IOS应用的全球唯一标识。无论代码怎么改,图标和应用名称怎么换,只要bundle id没变,ios系统就认为这是同一个应用。每开发一个新应用,首先都需要到member center->identifier->APP IDS去创建一个bundle id。Explicit App ID的格式是com.domainname.appname,这种id只能用在一个app上。每一个新应用都要创建一个,其中domainname可以使用公司的缩写,全拼。
1、登录苹果开发者网站,登录开发者账户。添加新的AppID,并填写相关的Name和Bundle ID。

2、为创建的APP ID 开启Push Notification功能,已有的appID也可以继续添加Push Notification功能。

3、完成以上操作后依次点击Continue,点击Register,完成APP ID的注册。
❷ 配置极光推送端需要的两种证书:开发证书,生产证书。
- 打开系统里自带的“钥匙串访问”,创建
CSR(Certificate Signing Request)文件 。填写用户邮箱和常用名称,并选择存储到磁盘,证书文件后缀为.certSigningRequest。


- 点击苹果开发者网站账户左侧的
development/Production,上传请求生成的CSR文件。生成证书后,点击downLoad将证书下载到本地中,后缀为.cer文件。点击生成的证书,在钥匙串中打开,导出为.p12文件,并存储到本地。



❸ 把配置好的证书,传递到极光开发平台上。
在极光官网申请好的极光推送账号里创建应用。点击极光开发者服务,找到推送设置,选择iOS,用下载好的开发证书和生产证书导入,填写设置好的p12证书密码,点击保存会生成一个appkey。集成极光推送SDK到项目里的时候会用到此appkey。
c、极光推送的消息形式
通知(APNS):手机的通知栏(状态栏)上会显示的一条通知信息。
自定义消息(应用内消息):不会被 SDK 展示到通知栏上。自定义消息主要用于应用的内部业务逻辑。朋友圈红点就可以用这个。极光推送采用的是长连接,所以自定义消息在网络正常、App处于前台的情况下会马上收到。
本地通知:SDK集成苹果实现本地通知。
d、极光推送的实现原理
通过我们的App服务器或极光Web端调用极光的API能发起极光推送。举个例子,用户A(userIdA)发消息给用户B(userIdB)。这里只考虑两个都绑定好了deviceToken等,不存在离线消息。
苹果原生态下的流程图

极光下的流程图

e、JPush APNS通知的意义
iOS平台上推送通知,只有APNS这个官方的通道,是可以随时送达的。一般开发者都是自己部署应用服务器向APNS Server推送。JPush推送相比直接向APNS推送有什么好处呢?
减少开发及维护成本
- 应用开发者不需要去开发维护自己的推送服务器与
APNS对接 - 集成了
JPush SDK后不必自己维护更新device token - 通过
JPush的WebPortal直接推送,也可以调用JPush的HTTP协议API来完成,开发工作量大大减少
减少运营成本
- 极光推送支持一次推送,同时向Android和iOS平台。支持统一的
API与推送界面 - 极光推送提供标签、别名绑定机制,以及提供了非常细分的用户分群方式,运营起来非常简单、直观
提供应用内推送
- 除了使得
APNS推送更简单,也另外提供应用内消息推送,这在类似于聊天的场景里很有必要
2、项目中极光SDK的配置
a、导入极光SDK
方法1:可以通过CocoaPods进行导入JPush。
方法2:手动导入可以参考极光文档-iOS SDK集成指南。
b、进入项目中的appDelegate导入头文件,遵循代理。
#import "JPUSHService.h"// 引入JPush功能所需头文件
#import <UserNotifications/UserNotifications.h>// 注册APNs所需头文件
@interface JpushManager ()<JPUSHRegisterDelegate,UNUserNotificationCenterDelegate>
@end
c、在didFinishLaunchingWithOptions中进行JPush的相关初始化设置
在didFinishLaunching方法中调用极光推送的配置方法
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// 极光推送
[self configureJpushWithLaunchingOption:launchOptions];
return YES;
}
声明配置过程中使用到的全局常量
static NSString * const JPushAppKey = @"e98bc4beea9b2988976bae04";// 极光appKey
static NSString * const JPushChannel = @"Publish channel";// 固定的
// NO为开发环境,YES为生产环境。虚拟机和真机调试属于开发环境。测试包、企业包和App Store属于生产环境
BOOL isProduction = !DEBUG;
BOOL kFUserJPush = NO;
实现极光推送的配置方法
- (void)configureJpushWithLaunchingOption:(NSDictionary *)launchingOption
{
// 初始化推送
[[JpushManager shareManager] setupJPushWithLaunchingOption:launchingOption appKey:JPushAppKey channel:JPushChannel apsForProduction:isProduction advertisingIdentifier:nil];
// 设置角标为0
[[JpushManager shareManager] setBadge:0];
__weak __typeof(self)weakSelf = self;
[JpushManager shareManager].afterReceiveNoticationHandle = ^(NSDictionary *userInfo){
NSLog(@"接收到消息后处理消息");
[weakSelf getMessageToHandle];
};
}
初始化推送
- (void)setupJPushWithLaunchingOption:(NSDictionary *)launchingOption appKey:(NSString *)appKey channel:(NSString *)channel apsForProduction:(BOOL)isProduction advertisingIdentifier:(NSString *)advertisingId;
{
// 添加APNs代码 注册极光
JPUSHRegisterEntity *entity = [[JPUSHRegisterEntity alloc] init];
entity.types = JPAuthorizationOptionAlert|JPAuthorizationOptionBadge|JPAuthorizationOptionSound;
[JPUSHService registerForRemoteNotificationConfig:entity delegate:self];
// 可以添加自定义categories
NSSet<UNNotificationCategory *> *categories;
entity.categories = categories;
// IDFA为设备广告标示符,用于广告投放。通常不会改变,不同App获取到都是一样的。但如果用户完全重置系统((设置程序 -> 通用 -> 还原 -> 还原位置与隐私) ,这个广告标示符会重新生成。
// IDFA用于同一设备下的不同app信息共享,如不需要使用,advertisingIdentifier 可为nil
// NSString *advertisingId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
[JPUSHService setupWithOption:launchingOption appKey:appKey channel:channel apsForProduction:isProduction advertisingIdentifier:advertisingId];
// 获取极光推送注册ID(RegistrationID)
// 原生是采用deviceToken来标识设备唯一性。在极光中采用RegistrationID
// 其生成原则优先采用IDFA(如果设备未还原IDFA,卸载App后重新下载,还是能被识别出老用户),次采用deviceToken
// 集成了 JPush SDK 的应用程序在第一次 App 启动后,成功注册到 JPush 服务器时,JPush 服务器会给客户端返回唯一的该设备的标识 -—— RegistrationID
[JPUSHService registrationIDCompletionHandler:^(int resCode, NSString *registrationID) {
if(resCode == 0)
{
NSLog(@"registrationID 获取成功为:%@",registrationID);
// 设置别名
// 一个设备只能有一个别名(Alias),但能有多个标签。所以别名可以用userId,针对一个用户
// 标签(Tag)可以用用户所处分组,方便针对目标用户推送,针对一批用户
[JPUSHService setAlias:[[NSUserDefaults standardUserDefaults] valueForKey:@"userId"] completion:^(NSInteger iResCode, NSString *iAlias, NSInteger seq) {
NSLog(@"设置别名");
} seq:0];
}
else
{
NSLog(@"registrationID 获取失败,code为:%d",resCode);
}
}];
}
d、注册DevieceToken
远端推送需要获取设备的Device Token
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSLog(@"设备的Device Token为:%@", deviceToken);
// 极光推送注册 DeviceToken
[[JpushManager shareManager] registerDeviceToken:deviceToken];
}
在appdelegate注册设备处调用极光推送注册 DeviceToken
- (void)registerDeviceToken:(NSData *)deviceToken
{
[JPUSHService registerDeviceToken:deviceToken];
}
3、JPUSHRegisterDelegate
a、收到通知消息后展示
// 收到推送的消息后的回调
typedef void(^AfterReceiveNoticationHandle)(NSDictionary *userInfo);
/** 接收到消息后的处理 */
@property(copy,nonatomic) AfterReceiveNoticationHandle afterReceiveNoticationHandle;
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler
{
NSDictionary *userInfo = notification.request.content.userInfo;
if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]])
{
[JPUSHService handleRemoteNotification:userInfo];
if (self.afterReceiveNoticationHandle)
{
self.afterReceiveNoticationHandle(userInfo);
}
}
// 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Banner三种类型可以设置
completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionSound|UNNotificationPresentationOptionBanner);
}
b、点击通知进入App
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
{
NSDictionary * userInfo = response.notification.request.content.userInfo;
UNNotificationRequest *request = response.notification.request;// 收到推送的请求
UNNotificationContent *content = request.content;// 收到推送的消息内容
NSNumber *badge = content.badge;// 推送消息的角标
NSString *body = content.body;// 推送消息体
UNNotificationSound *sound = content.sound;// 推送消息的声音
NSString *subtitle = content.subtitle;// 推送消息的副标题
NSString *title = content.title;// 推送消息的标题
NSLog(@"点击通知栏,收到远程通知的用户信息为:%@", userInfo);
NSLog(@"解析后信息为:{\nbody:%@,\ntitle:%@,\nsubtitle:%@,\nbadge:%@,\nsound:%@,\nuserInfo:%@\n}",body,title,subtitle,badge,sound,userInfo);
// 清空Jpush中存储的badge值
[self setBadge:0];
if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]])// 远程通知
{
NSLog(@"远程通知");
}
else// 本地通知
{
NSLog(@"本地通知");
}
[JPUSHService handleRemoteNotification:userInfo];
// 点击消息进行跳转到消息的详情界面中
// [self goToMssageViewControllerWith:userInfo];
// 系统要求执行这个方法
completionHandler();
}
c、点击通知打开设置APP
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(UNNotification *)notification
{
if (notification && [notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]])
{
NSLog(@"通知界面进入应用");
}
else
{
NSLog(@"设置界面进入应用");
}
}
4、封装的便利方法
a、设置角标
- (void)setBadge:(int)badge
{
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge];
[JPUSHService setBadge:badge];
}
b、设置别名
- (void)setAlias:(NSString *)aliasName
{
[JPUSHService getAlias:^(NSInteger iResCode, NSString *iAlias, NSInteger seq) {
NSLog(@"旧的别名为:iResCode == %ld,iAlias == %@",(long)iResCode,iAlias);
if (![iAlias isEqualToString:aliasName])
{
[JPUSHService setAlias:aliasName completion:^(NSInteger iResCode, NSString *iAlias, NSInteger seq) {
NSLog(@"设置新的别名:callBackTextView %@",[NSString stringWithFormat:@"iResCode:%ld, \niAlias: %@, \nseq: %ld\n", (long)iResCode, iAlias, (long)seq]);
} seq:0];
}
} seq:0];
}
c、删除别名
- (void)deleteAlias
{
[JPUSHService deleteAlias:^(NSInteger iResCode, NSString *iAlias, NSInteger seq) {
NSLog(@"删除别名");
} seq:0];
}
d、接收到消息后的处理
__weak __typeof(self)weakSelf = self;
[JpushManager shareManager].afterReceiveNoticationHandle = ^(NSDictionary *userInfo){
NSLog(@"接收到消息后处理消息");
[weakSelf getMessageToHandle];
};
// 接收到消息后处理消息
- (void)getMessageToHandle
{
NSLog(@"这条消息价值百万英镑!!!")
}
Demo
Demo在我的Github上,欢迎下载。
BasicsDemo
