原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的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:意图标识符 可在 中查看,主要是针对电话、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 *requests))completionHandler;
//删除所有未送达的特定id的消息
- (void)removePendingNotificationRequestsWithIdentifiers:(NSArray *)identifiers;
//删除所有未送达的消息
- (void)removeAllPendingNotificationRequests;
//获取已送达的所有消息列表
- (void)getDeliveredNotificationsWithCompletionHandler:(void(^)(NSArray *notifications))completionHandler;
//删除所有已送达的特定id的消息
- (void)removeDeliveredNotificationsWithIdentifiers:(NSArray *)identifiers;
//删除所有已送达的消息
- (void)removeAllDeliveredNotifications;
调用方式如下:
- (void)removeNotificaiton
{
NSString *requestIdentifier = @"XieJiaPei";
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
// 删除设备已收到特定id的所有消息推送
[center removeDeliveredNotificationsWithIdentifiers:@[requestIdentifier]];
// 删除设备已收到的所有消息推送
[center removeAllDeliveredNotifications];
// 获取设备已收到的消息推送
[center getDeliveredNotificationsWithCompletionHandler:^(NSArray * _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:,
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] 通知的配置信息:
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
@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:意图标识符 可在 中查看,主要是针对电话、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 ()
@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 // 注册APNs所需头文件
@interface JpushManager ()
@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 *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
参考文献
- iOS极光推送开发入门
- iOS 推送通知及通知扩展