原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的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
- 参考文献
一、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、准备工作
a、推送证书和权限
- 根据工程的
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
a、Pusher的下载地址
NWPusher可以可以当做framework
使用,也可以直接下载APP使用。
b、Pusher的使用步骤
使用Pusher
工具模拟App Server
将指定的device token
和消息内容发送给APNs
。

- 选择p12格式的推送证书
- 设置是否为测试环境(默认勾选为测试环境,由于推送证书分为测试证书和生产证书,并且苹果的
APNs
也分为测试和生产两套环境,因此Pusher
需要手动勾选推送环境) - 输入
device token
- 输入符合苹果要求格式的
aps
字符串 - 执行推送
c、内容格式
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:
value
是我们需要传送的内容 -
alert:
value
就是弹出框需要展示的内容 -
badge:
value
就是APP icon
,展示的信息个数 -
sound:
value
就是表示当有Push
消息的时候,是否需要声音提示
d、稍纵即逝你就收到了远端消息了
可惜没开发者账户无法测试,连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
{
self.contentHandler(self.bestAttemptContent);
}
Notification Content
// 在这儿做界面初始化的工作
// 不能插入新的event,纯代码addTarget的event无法使用
- (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
#import "NotificationService.h"
#import <UIKit/UIKit.h>
@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
{
// 将获取到的内容传递给content扩展
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的相关初始化设置
初始化推送
- (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);
}
}];
}
didFinishLaunching中极光推送的配置
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// 极光推送
[self configureJpushWithLaunchingOption:launchOptions];
return YES;
}
- (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];
};
}
d、注册DevieceToken
// 在appdelegate注册设备处调用
- (void)registerDeviceToken:(NSData *)deviceToken
{
[JPUSHService registerDeviceToken:deviceToken];
}
// 远端推送需要获取设备的Device Token
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSLog(@"设备的Device Token为:%@", deviceToken);
// 极光推送注册 DeviceToken
[[JpushManager shareManager] registerDeviceToken:deviceToken];
}
3、JPUSHRegisterDelegate
a、收到通知消息后展示
- (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、接收到消息后的处理
// 收到推送的消息后的回调
typedef void(^AfterReceiveNoticationHandle)(NSDictionary *userInfo);
/** 接收到消息后的处理 */
@property(copy,nonatomic) AfterReceiveNoticationHandle afterReceiveNoticationHandle;
// 传递消息
NSDictionary *userInfo = notification.request.content.userInfo;
if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]])
{
[JPUSHService handleRemoteNotification:userInfo];
if (self.afterReceiveNoticationHandle)
{
self.afterReceiveNoticationHandle(userInfo);
}
}
__weak __typeof(self)weakSelf = self;
[JpushManager shareManager].afterReceiveNoticationHandle = ^(NSDictionary *userInfo){
NSLog(@"接收到消息后处理消息");
[weakSelf getMessageToHandle];
};
// 接收到消息后处理消息
- (void)getMessageToHandle
{
NSLog(@"这条消息价值百万英镑!!!")
}
Demo
Demo在我的Github上,欢迎下载。
PushServiceDemo
网友评论