原創(chuàng):知識點總結(jié)性文章
創(chuàng)作不易夯到,請珍惜峦萎,之后會持續(xù)更新,不斷完善
個人比較喜歡做筆記和寫總結(jié)蝶桶,畢竟好記性不如爛筆頭哈哈慨绳,這些文章記錄了我的IOS成長歷程,希望能與大家一起進步
溫馨提示:由于簡書不支持目錄跳轉(zhuǎn)真竖,大家可通過command + F 輸入目錄標題后迅速尋找到你所需要的內(nèi)容
目錄
- 一脐雪、簡介
- 一、Local Notifications(本地推送)
- 1恢共、本地推送流程
- 2战秋、通知的觸發(fā)條件
- 3、通知的內(nèi)容
- 4讨韭、對推送進行查脂信、改、刪
- 5拐袜、UNUserNotificationCenterDelegate中的回調(diào)方法
- 二吉嚣、Remote Notifications(遠程推送)
- 1梢薪、遠程推送流程
- 2蹬铺、準備工作
- 3、AppDelegate中的方法
- 4秉撇、App Server
- 三甜攀、iOS 通知擴展
- 1、準備工作
- 2琐馆、通知服務(wù)擴展(UNNotificationServiceExtension)
- 3规阀、通知內(nèi)容擴展(UNNotificationContentExtension)
- 四、極光推送
- 1瘦麸、簡介
- 2谁撼、項目中極光SDK的配置
- 3、JPUSHRegisterDelegate
- 4滋饲、封裝的便利方法
- Demo
- 參考文獻
一厉碟、簡介
日常生活中會有很多種情形需要通知喊巍,比如:新聞提醒、定時吃藥箍鼓、定期體檢崭参、到達某個地方提醒用戶等等,這些功能在 UserNotifications
中都提供了相應的接口款咖。
iOS推送分為Local Notifications
(本地推送) 和 Remote Notifications
(遠程推送)何暮。本地推送是App本地創(chuàng)建通知,加入到系統(tǒng)的Schedule
里铐殃,如果觸發(fā)器條件達成時會推送相應的消息內(nèi)容海洼。
在遠程推送中,Provider
是指某個APP的Push
服務(wù)器富腊。APNS
是Apple Push Notification Service
(Apple Push
服務(wù)器)的縮寫贰军,是蘋果的服務(wù)器。APNS Pusher
應用程序把要發(fā)送的消息蟹肘、目標iPhone
的標識(deviceToken
)打包词疼,發(fā)給APNS
。APNS
在自身的已注冊Push
服務(wù)的iPhone
列表中帘腹,查找有相應標識的iPhone
贰盗,并把消息發(fā)到iPhone
。iPhone
把發(fā)來的消息傳遞給相應的應用程序阳欲, 并且按照設(shè)定彈出Push
通知舵盈。
如果你的App有遠端推送的話,那你需要用開發(fā)者賬號新建一個push
證書球化。再在Capabilities
中打開Push Notifications
開關(guān)秽晚,打開后會自動在項目里生成entitlements
文件。
二筒愚、Local Notifications(本地推送)
1赴蝇、本地推送流程
本地推送通知是由本地應用觸發(fā)的,是基于時間的通知形式巢掺,一般用于鬧鐘定時句伶、待辦事項等提醒功能。
a陆淀、發(fā)送本地推送通知的步驟
- 創(chuàng)建一個觸發(fā)器(
trigger
) - 創(chuàng)建推送的內(nèi)容(
UNMutableNotificationContent
) - 創(chuàng)建推送請求(
UNNotificationRequest
) - 推送請求添加到推送管理中心(
UNUserNotificationCenter
)中
b考余、步驟的代碼實現(xiàn)
- (void)simpleLocalNotificationDescribe
{
[self buildNotificationDescribe:@"最簡單的本地通知(創(chuàng)建5秒后觸發(fā),建議回到桌面察看效果)"];
}
- (void)simpleLocalPushService
{
// 1.定時推送
UNTimeIntervalNotificationTrigger *trigger = [self getTimeTrigger];
// 2.推送的內(nèi)容
UNMutableNotificationContent *content = [self getSimpleContent];
// 3.創(chuàng)建通知請求 UNNotificationRequest 將觸發(fā)條件和通知內(nèi)容添加到請求中
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轧苫、通知的觸發(fā)條件
蘋果把本地通知跟遠程通知合二為一楚堤。區(qū)分本地通知跟遠程通知的類是UNNotificationTrigger
,通過它,我們可以得到一些通知的觸發(fā)條件身冬。
UNPushNotificationTrigger// 遠程推送的通知類型
UNTimeIntervalNotificationTrigger// (本地通知) 一定時間之后鳄袍,重復或者不重復推送通知。我們可以設(shè)置timeInterval(時間間隔)和repeats(是否重復)
UNCalendarNotificationTrigger//(本地通知) 一定日期之后吏恭,重復或者不重復推送通知 例如拗小,你每天8點推送一個通知,只要dateComponents為8樱哼,如果你想每天8點都推送這個通知哀九,只要repeats為YES就可以了
UNLocationNotificationTrigger// (本地通知)地理位置的一種通知,當用戶進入或離開一個地理區(qū)域來通知
定時推送
// 觸發(fā)推送的時機搅幅。timeInterval:單位為秒(s) repeats:是否循環(huán)提醒
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創(chuàng)建位置信息
CLLocationCoordinate2D center1 = CLLocationCoordinate2DMake(39.788857, 116.5559392);
CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center1 radius:500 identifier:@"海峽國際社區(qū)"];
// 進入地區(qū)、從地區(qū)出來或者兩者都要的時候進行通知
region.notifyOnEntry = YES;
region.notifyOnExit = YES;
// region 位置信息 repeats 是否重復
UNLocationNotificationTrigger *locationTrigger = [UNLocationNotificationTrigger triggerWithRegion:region repeats:YES];
3茄唐、通知的內(nèi)容
a息裸、文字、圖像沪编、聲音
- (UNMutableNotificationContent *)getSimpleContent
{
// 推送的文本內(nèi)容
// UNNotificationContent的屬性readOnly呼盆,而UNMutableNotificationContent的屬性可以更改
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
// 限制在一行,多出部分省略號
content.title = @"時間提醒";
content.subtitle = [NSString stringWithFormat:@"《請回答1988》第二季放映的時間提醒"];
// body中printf風格的轉(zhuǎn)義字符蚁廓,比如說要包含%访圃,需要寫成%% 才會顯示,\同樣
content.body = @"口若懸河相嵌、妙語迭出的精彩表演腿时,從作者津津樂道的口吻可以看出,王爾德無疑是在顧影自憐饭宾,因為他自己正是這樣的作秀高手批糟,而他在社交圈中越練越“酷”的口才,在他的社會喜劇中得到了淋漓盡致的發(fā)揮看铆,令觀眾如醉如癡!";
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)
{
// 這里設(shè)置的是Array,但是只會取lastObject
content.attachments = @[imageAttachment];
}
}
return content;
}
b性湿、視頻
- (UNMutableNotificationContent *)getVideoContent
{
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
content.title = @"WWDC";
content.subtitle = @"蘋果開發(fā)者技術(shù)大會";
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)
{
// 這里設(shè)置的是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:@"發(fā)送" textInputPlaceholder:@"說說今天發(fā)生了啥......"];
[actionMutableArray addObjectsFromArray:@[unUnlockAction, destructiveAction, inputTextAction]];
if (actionMutableArray.count > 1)
{
/**categoryWithIdentifier方法
* identifier:是這個category的唯一標識侠讯,用來區(qū)分多個category,這個id不管是Local Notification暑刃,還是remote Notification厢漩,一定要有并且要保持一致
* actions:創(chuàng)建action的操作數(shù)組
* intentIdentifiers:意圖標識符 可在 <Intents/INIntentIdentifiers.h> 中查看,主要是針對電話岩臣、carplay 等開放的 API
* options:通知選項 枚舉類型 也是為了支持 carplay
*/
UNNotificationCategory *categoryNotification = [UNNotificationCategory categoryWithIdentifier:@"categoryOperationAction" actions:actionMutableArray intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];
// 將創(chuàng)建的 category 添加到通知中心
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObject:categoryNotification]];
// category的唯一標識溜嗜,Local Notification保持一致
content.categoryIdentifier = @"categoryOperationAction";
}
return content;
}
4、對推送進行查架谎、改炸宵、刪
a、更新通知
Local Notification
重新創(chuàng)建具有相同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;
調(diào)用方式如下:
- (void)removeNotificaiton
{
NSString *requestIdentifier = @"XieJiaPei";
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
// 刪除設(shè)備已收到特定id的所有消息推送
[center removeDeliveredNotificationsWithIdentifiers:@[requestIdentifier]];
// 刪除設(shè)備已收到的所有消息推送
[center removeAllDeliveredNotifications];
// 獲取設(shè)備已收到的消息推送
[center getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
NSLog(@"獲取設(shè)備已收到的消息推送");
}];
}
5、UNUserNotificationCenterDelegate中的回調(diào)方法
a末秃、即將展示推送的通知時觸發(fā)(app在前臺獲取到通知)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
{
// 收到推送的請求
UNNotificationRequest *request = notification.request;
// 收到的內(nèi)容
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);
}
// 不管前臺后臺狀態(tài)下。推送消息的橫幅都可以展示出來
// 需要執(zhí)行這個方法贺待,選擇是否提醒用戶徽曲,有Badge、Sound麸塞、Banner三種類型可以設(shè)置
completionHandler(UNNotificationPresentationOptionBadge|
UNNotificationPresentationOptionSound|
UNNotificationPresentationOptionBanner);
}
本地通知的輸出結(jié)果
2020-10-26 16:08:53.383283+0800 PushServiceDemo[71205:2358029] 簡單的本地通知已添加成功!
2020-10-26 16:08:58.393934+0800 PushServiceDemo[71205:2357722] 本地推送的通知:{
body:口若懸河秃臣、妙語迭出的精彩表演,從作者津津樂道的口吻可以看出哪工,王爾德無疑是在顧影自憐奥此,因為他自己正是這樣的作秀高手,而他在社交圈中越練越“酷”的口才雁比,在他的社會喜劇中得到了淋漓盡致的發(fā)揮稚虎,令觀眾如醉如癡!,
title:時間提醒,
subtitle:《請回答1988》第二季放映的時間提醒,
badge:5偎捎,
sound:<UNNotificationSound: 0x6000038f1a40>蠢终,
userInfo:{
age = 22;
useName = XieJiapei;
}}
b序攘、用戶點擊推送消息時觸發(fā) (點擊通知進入app時觸發(fā))
- (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(@"紅色顯示寻拂,并且設(shè)置APP的Badge通知數(shù)字為0");
// 設(shè)置APP的Badge通知數(shù)字
[[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];
});
}
}
// 系統(tǒng)要求執(zhí)行這個方法
completionHandler();
}
點擊紅色按鈕的輸出結(jié)果為:
2020-10-27 16:20:02.797425+0800 PushServiceDemo[80030:2808431] 紅色顯示程奠,并且設(shè)置APP的Badge通知數(shù)字為0
二、Remote Notifications(遠程推送)
1祭钉、遠程推送流程
遠程推送通知是通過蘋果的APNs
(Apple Push Notification service
)發(fā)送到app瞄沙,而APNs
必須先知道用戶設(shè)備的令牌(device token
)。在啟動時慌核,app與APNs
通信并接收device token
帕识,然后將其轉(zhuǎn)發(fā)到App Server
,App Server
將該令牌和要發(fā)送的通知消息發(fā)送至APNs
遂铡。
蘋果官方提供的遠程推送通知的傳遞示意圖如下:
各關(guān)鍵組件之間的交互細節(jié):
2肮疗、準備工作
- 根據(jù)工程的
Bundle Identifier
,在蘋果開發(fā)者平臺中創(chuàng)建同名App ID
扒接,并勾選Push Notifications
服務(wù) - 在工程的
Capabilities
中啟動Push Notifications
- 遠程推送必須使用真機調(diào)試伪货,因為模擬器無法獲取得到
device token
想要為蘋果開發(fā)軟件并上架蘋果商店,就需要參加蘋果開發(fā)者計劃钾怔,需要交納年費(699兩檔)碱呼,只要求自己編寫的代碼在蘋果真機上跑起來,只需要注冊成蘋果開發(fā)者賬戶就可以了宗侦,不需要交錢愚臀,但是如果想調(diào)試推送惨奕、iCloud蚤氏、IAP之類的功能靶衍,或者上架蘋果商店鸟辅,就需要交錢了。買不起~~~~無法調(diào)試呀??以后有開發(fā)者賬戶了再看看吧~生成APNs后端推送證書
3北戏、AppDelegate中的方法
a尿赚、注冊遠程通知
- (void)registerPushService
{
// 遠程通知授權(quán)
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(@"遠程通知中心打開失敗");
}
}];
// 獲取注冊之后的權(quán)限設(shè)置
// 注意UNNotificationSettings是只讀對象督惰,不能直接修改
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
NSLog(@"通知的配置信息:\n%@",settings);
}];
}
輸出結(jié)果為:
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
發(fā)送給App Server
茴厉。只有蘋果公司知道device token
的生成算法,保證唯一什荣。device token
在app卸載后重裝等情況時會變化矾缓,因此為確保device token
變化后app仍然能夠正常接收服務(wù)器端發(fā)送的通知,建議每次啟動應用都將獲取到的device token
傳給App Server
稻爬。
// 遠端推送需要獲取設(shè)備的Device Token
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
// 解析NSData獲取字符串
NSString *deviceString = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];// 移除<>
deviceString = [deviceString stringByReplacingOccurrencesOfString:@" " withString:@""];// 移除空格
NSLog(@"設(shè)備的Device Token為:%@",deviceString);
}
// 獲取設(shè)備的DeviceToken失敗
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
NSLog(@"獲取設(shè)備的DeviceToken失斒任拧:%@\n",error.description);
}
4、App Server
Pusher的下載地址
NWPusher可以當做framework
使用因篇,也可以直接下載APP使用泞辐。
Pusher的使用步驟
使用Pusher
工具模擬App Server
將指定的device token
和消息內(nèi)容發(fā)送給APNs
笔横。
- 選擇p12格式的推送證書
- 設(shè)置是否為測試環(huán)境竞滓。默認勾選為測試環(huán)境咐吼,由于推送證書分為測試證書和生產(chǎn)證書,并且蘋果的
APNs
也分為測試和生產(chǎn)兩套環(huán)境商佑,因此Pusher
需要手動勾選推送環(huán)境 - 輸入
device token
- 輸入符合蘋果要求格式的
aps
字符串 - 執(zhí)行推送
內(nèi)容格式
在Payload
中輸入的內(nèi)容就是我們需要傳送的數(shù)據(jù)了锯茄,這個數(shù)據(jù)傳輸以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:我們需要傳送的內(nèi)容
- alert:彈出框需要展示的內(nèi)容
- badge:展示的信息個數(shù)
-
sound:表示當有
Push
消息的時候茶没,是否需要聲音提示
稍縱即逝你就收到了遠端消息了
可惜沒開發(fā)者賬戶無法測試肌幽,連device token
都拿不到。
2020-10-27 15:06:34.967968+0800 PushServiceDemo[1272:220269] 獲取設(shè)備的DeviceToken失斪グ搿:Error Domain=NSCocoaErrorDomain Code=3000 "未找到應用程序的“aps-environment”的授權(quán)字符串" UserInfo={NSLocalizedDescription=未找到應用程序的“aps-environment”的授權(quán)字符串}
三喂急、iOS 通知擴展
1、準備工作
a笛求、添加新的Target--> Notification Service/Content
b廊移、擴展工程的目錄
系統(tǒng)會自動創(chuàng)建一個 UNNotificationServiceExtension
的子類 NotificationService
。通過完善這個子類探入,來實現(xiàn)你的需求狡孔。NotificationViewController
直接繼承于ViewController
,因此可以在這個類中重寫相關(guān)方法蜂嗽,來修改界面的相關(guān)布局及樣式苗膝。
c、擴展提供的方法
Notification Service
讓你可以在后臺處理接收到的推送植旧,傳遞最終的內(nèi)容給 contentHandler
辱揭。系統(tǒng)接到通知后,有最多30秒在這里重寫通知內(nèi)容(如下載附件并更新通知)病附。
- (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);
}
在你獲得的一小段運行通知代碼的時間即將結(jié)束的時候界阁,如果仍然沒有成功的傳入內(nèi)容,會走到這個方法胖喳,可以在這里傳肯定不會出錯的內(nèi)容泡躯,或者默認傳遞原始的推送內(nèi)容。處理過程超時丽焊,則收到的通知直接展示出來较剃。
- (void)serviceExtensionTimeWillExpire
{
// 將獲取到的內(nèi)容傳遞給content擴展
self.contentHandler(self.bestAttemptContent);
}
Notification Content
在這兒做界面初始化的工作。
- (void)viewDidLoad
{
[super viewDidLoad];
}
獲取通知信息技健,更新UI控件中的數(shù)據(jù)写穴。
- (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、調(diào)整 info.plist
使用自定義的NotificationContent
的時候雌贱,需要對應extension
中info.plist
啊送,因為推送通知內(nèi)容中的category
字段偿短,與UNNotificationContentExtension
的info.plist
中UNNotificationExtensionCategory
字段的值要匹配,系統(tǒng)才能找到自定義的UI
馋没。
categoryIdentifier
UNNotificationExtensionCategory
默認是string
類型昔逗,可以手動更改成array
類型,array
中的item
(string
)是categoryName
篷朵。在收到通知的時候勾怒,我們可以讓服務(wù)器把這個通知的categoryIdentifier
帶上,作用是我們可以根據(jù)視頻声旺、音樂笔链、圖片來分別自定義我們的通知內(nèi)容。不同的分類標識符腮猖,也會在使用UNNotificationAction
的時候幫助我們區(qū)分是什么類型的通知鉴扫,方便我們對不同類型的通知做出不同的操作行為。我們目前在Service
澈缺、Content
坪创、aps
寫死了categoryIdentifier
,其實在收到系統(tǒng)推送時谍椅,每一個推送內(nèi)容最好帶上一個跟服務(wù)器約定好了的categoryIdentifier
误堡,這樣方便我們根據(jù)categoryIdentifier
來自定義不同類型的視圖,以及action
雏吭。
UNNotificationExtensionInitialContentSizeRatio
UNNotificationExtensionInitialContentSizeRatio
這個值必須要有锁施,類型是一個浮點類型,代表的是高度與寬度的比值杖们。系統(tǒng)會使用這個比值悉抵,作為初始化view
的大小。舉個簡單的例子來說摘完,如果該值為1姥饰,則該視圖為正方形。如果為0.5孝治,則代表高度是寬度的一半列粪。注意這個值只是初始化的一個值,在這個擴展添加后谈飒,可以重寫frame岂座,展示的時候,在我們還沒打開這個視圖預覽時杭措,背景是個類似圖片占位的灰色费什,那個灰色的高度寬度之比,就是通過這個值來設(shè)定手素。
UNNotificationExtensionDefaultContentHidden
UNNotificationExtensionDefaultContentHidden
這個值可選鸳址,是一個BOOL
值瘩蚪。當為YES
時,會隱藏上方原本推送的內(nèi)容視圖稿黍,只會顯示我們自定義的視圖疹瘦,因為在自定義視圖的時候闻察,我們可以取得推送內(nèi)容辕漂,然后按照我們想要的布局吴超,展示出來钉嘹。如果為NO
時(默認為NO
),推送視圖就會既有我們的自定義視圖鲸阻,也會有系統(tǒng)原本的推送內(nèi)容視圖(這里附件是不會顯示的跋涣,只會顯示body
里面的文字喲)。這里需要隱藏默認消息框鸟悴,所以添加UNNotificationExtensionDefaultContentHidden
屬性陈辱,Bool(YES)
。
NSExtensionMainStoryboard
至于NSExtensionMainStoryboard
以及NSExtensionPointIdentifier
细诸,系統(tǒng)默認生成沛贪,大家直接用就好,如果需要更改的震贵,只能更改使用的storyboard
的名字(不過應該沒人會把系統(tǒng)的刪除再建立一個吧 O(∩_∩)O)
最初的info.plist
修改成array后的info.plist
修改后Service和aps的info.plist
2利赋、通知服務(wù)擴展(UNNotificationServiceExtension)
a、簡介
支持附帶 Media Attachments
本地推送和遠程推送同時都可支持附帶Media Attachments
猩系。不過遠程通知需要實現(xiàn)通知服務(wù)擴展UNNotificationServiceExtension
媚送,在service extension
里面去下載attachment
,但是需要注意吟秩,service
extension
會限制下載的時間(30s)收恢,并且下載的文件大小也會同樣被限制火窒。這里畢竟是一個推送,而不是把所有的內(nèi)容都推送給用戶褪储。所以你應該去推送一些縮小比例之后的版本昔榴。比如圖片吱肌,推送里面附帶縮略圖坟瓢,當用戶打開app之后粒褒,再去下載完整的高清圖。視頻就附帶視頻的關(guān)鍵幀或者開頭的幾秒,當用戶打開app之后再去下載完整視頻苛萎。
UNNotificationAttachment 支持的附件格式和大小限制
- 音頻5M(
kUTTypeWaveformAudio
/kUTTypeMP3
/kUTTypeMPEG4Audio
/kUTTypeAudioInterchangeFileFormat
) - 圖片10M(
kUTTypeJPEG
/kUTTypeGIF/kUTTypePNG
) - 視頻50M(
kUTTypeMPEG
/kUTTypeMPEG2Video
/kUTTypeMPEG4
/kUTTypeAVIMovie
)
校驗附件
系統(tǒng)會在通知注冊前校驗附件,如果附件出問題,通知注冊失敻笪!吟吝;校驗成功后,附件會轉(zhuǎn)入attachment data store
蛹磺;如果附件是在app bundle
俗批,則是會被copy
來取代move
辛慰。attachment data store
的位置麻汰?利用代碼測試獲取在磁盤上的圖片文件作為attachment
,會發(fā)現(xiàn)注冊完通知后智亮,圖片文件被移除,在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秒重寫通知內(nèi)容
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler
{
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
// 修改通知的內(nèi)容
self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [ServiceExtension modified]", self.bestAttemptContent.title];
// 設(shè)置UNNotificationAction
[self getAction];
// category的唯一標識,Remote Notification保持一致
self.bestAttemptContent.categoryIdentifier = @"categoryOperationAction";
// 加載網(wǎng)絡(luò)請求
NSDictionary *userInfo = self.bestAttemptContent.userInfo;
NSString *mediaUrl = userInfo[@"media"][@"url"];
NSString *mediaType = userInfo[@"media"][@"type"];
if (!mediaUrl.length)// 不存在url則使用基本的內(nèi)容
{
self.contentHandler(self.bestAttemptContent);
}
else// 否則使用網(wǎng)絡(luò)請求到的內(nèi)容
{
// 創(chuàng)建附件資源
// UNNotificationAttachment的url接收的是本地文件的url
// 附件資源必須存在本地拗慨,如果是遠程推送的網(wǎng)絡(luò)資源需要提前下載到本地
[self loadAttachmentForUrlString:mediaUrl withType:mediaType completionHandle:^(UNNotificationAttachment *attach) {
if (attach)
{
// 將附件資源添加到 UNMutableNotificationContent 中
self.bestAttemptContent.attachments = [NSArray arrayWithObject:attach];
}
self.contentHandler(self.bestAttemptContent);
}];
}
}
將獲取到的內(nèi)容傳遞給content擴展
- (void)serviceExtensionTimeWillExpire
{
self.contentHandler(self.bestAttemptContent);
}
網(wǎng)絡(luò)請求
- (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];
// 從網(wǎng)絡(luò)下載媒體資源
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];
}
用于將媒體類型的后綴添加到文件路徑上
// 服務(wù)端在處理推送內(nèi)容時醋闭,最好加上媒體類型字段
- (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:@"發(fā)送" textInputPlaceholder:@"說說今天發(fā)生了啥......"];
NSArray *identifierArray = [[NSArray alloc] initWithObjects:@"IdentifierNeedUnUnlock", @"IdentifierRed", @"IdentifierInputText", nil];
[actionMutableArray addObjectsFromArray:@[unUnlockAction, destructiveAction, inputTextAction]];
if (actionMutableArray.count > 1)
{
/**categoryWithIdentifier方法
* identifier:是這個category的唯一標識侵蒙,用來區(qū)分多個category犁功,這個id不管是Local Notification案糙,還是remote Notification限嫌,一定要有并且要保持一致
* actions:創(chuàng)建action的操作數(shù)組
* intentIdentifiers:意圖標識符 可在 <Intents/INIntentIdentifiers.h> 中查看,主要是針對電話时捌、carplay 等開放的 API
* options:通知選項 枚舉類型 也是為了支持 carplay
*/
UNNotificationCategory *categoryNotification = [UNNotificationCategory categoryWithIdentifier:@"categoryOperationAction" actions:actionMutableArray intentIdentifiers:identifierArray options:UNNotificationCategoryOptionCustomDismissAction];
// 將創(chuàng)建的 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
在使用時需要配置相關(guān)證書,我沒有開發(fā)者賬號,所以無法調(diào)試苗沧。??(買不起,貧窮限制了我的開發(fā)能力......)
? 要選擇相應的target
來運行工程宏榕。
? 加斷點調(diào)試怎么不走相應方法卜壕?一個朋友找了很長時間的原因發(fā)現(xiàn)是xcode
的問題译隘,那個朋友就是我......
3拗馒、通知內(nèi)容擴展(UNNotificationContentExtension)
a、在展示通知時展示一個自定義的用戶界面育特。
這個就是個簡單的storyboard
文件念搬,內(nèi)部有一個View
栈虚,這個View
就是在上面的圖層中的自定義View
視圖了熔酷。它與NotificationViewController
所綁定偎肃。
這里使用純代碼方式來創(chuàng)建界面朱监,所以需要刪除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);
// 設(shè)置控件邊框顏色
[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演示
接收到通知的內(nèi)容
// 生成時默認實現(xiàn)了UNNotificationContentExtension協(xié)議的方法
- (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時觸發(fā)(殺死/切到后臺喚起)
- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption))completion
{
[self.hintLabel setText:[NSString stringWithFormat:@"觸發(fā)了%@", 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(), ^{
// 必須設(shè)置completion擂送,否則通知不會消失
// UNNotificationContentExtensionResponseOptionDismiss 直接讓該通知消失
// UNNotificationContentExtensionResponseOptionDismissAndForwardAction 消失并傳遞按鈕信息給AppDelegate悦荒,是否進入App看Att的設(shè)置
completion(UNNotificationContentExtensionResponseOptionDismiss);
});
}
c、控制媒體文件的播放
枚舉 UNNotificationContentExtensionMediaPlayPauseButtonType
typedef NS_ENUM(NSUInteger, UNNotificationContentExtensionMediaPlayPauseButtonType) {
// 沒有播放按鈕
UNNotificationContentExtensionMediaPlayPauseButtonTypeNone,
// 有播放按鈕嘹吨,點擊播放之后搬味,按鈕依舊存在,類似音樂播放的開關(guān)
UNNotificationContentExtensionMediaPlayPauseButtonTypeDefault,
// 有播放按鈕躺苦,點擊后身腻,播放按鈕消失,再次點擊暫停播放后匹厘,按鈕恢復
UNNotificationContentExtensionMediaPlayPauseButtonTypeOverlay,
}
設(shè)置播放按鈕的屬性
// 設(shè)置播放按鈕的屬性
@property (nonatomic, readonly, assign) UNNotificationContentExtensionMediaPlayPauseButtonType mediaPlayPauseButtonType;
// 設(shè)置播放按鈕的frame
@property (nonatomic, readonly, assign) CGRect mediaPlayPauseButtonFrame;
// 設(shè)置播放按鈕的顏色
@property (nonatomic, readonly, copy) UIColor *mediaPlayPauseButtonTintColor;
// 開始跟暫停播放
- (void)mediaPlay;
- (void)mediaPause;
這些屬性都是readonly
的嘀趟,所以直接用self.
屬性去修改肯定是報錯的,所以我們能用的就只有get
方法了愈诚。
根據(jù)button
的類型她按,我們可以聯(lián)想到,如果button
沒有炕柔,這個播放開始暫停的方法也沒用了酌泰。如果有button
,自然我們就有了播放的操作匕累,我們得出了以下結(jié)論陵刹。一定要重寫它的frame
來確定他的位置。指定顏色來設(shè)置它的顯示顏色欢嘿。設(shè)置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
)是獨立的第三方云推送平臺狗热,致力于為全球移動應用開發(fā)者提供移動消息推送服務(wù)钞馁。極光,是國內(nèi)領(lǐng)先的移動大數(shù)據(jù)的服務(wù)商匿刮。擁有開發(fā)者服務(wù)僧凰、廣告服務(wù)和數(shù)據(jù)服務(wù)三大產(chǎn)品體系。開發(fā)者服務(wù)助力app精細運營僻焚,覆蓋極光推送允悦、極光IM、極光短信虑啤、極光統(tǒng)計隙弛、社會化分享、極光認證狞山,廣告服務(wù)全闷,數(shù)據(jù)服務(wù)。說白了萍启,極光推送類似于我們每天在手機上接收到的消息总珠。那我們怎么在自己的app中實現(xiàn)這種功能呢?
b勘纯、準備證書和權(quán)限
? 創(chuàng)建APP ID
APP ID
是每一個IOS應用的全球唯一標識局服。無論代碼怎么改,圖標和應用名稱怎么換驳遵,只要bundle id
沒變淫奔,ios系統(tǒng)就認為這是同一個應用。每開發(fā)一個新應用堤结,首先都需要到member center
->identifier
->APP IDS
去創(chuàng)建一個bundle id
唆迁。Explicit App ID
的格式是com.domainname.appname
,這種id
只能用在一個app
上竞穷。每一個新應用都要創(chuàng)建一個唐责,其中domainname
可以使用公司的縮寫,全拼瘾带。
1鼠哥、登錄蘋果開發(fā)者網(wǎng)站,登錄開發(fā)者賬戶。添加新的AppID
肴盏,并填寫相關(guān)的Name
和Bundle ID
科盛。
2、為創(chuàng)建的APP ID
開啟Push Notification
功能菜皂,已有的appID
也可以繼續(xù)添加Push Notification
功能。
3厉萝、完成以上操作后依次點擊Continue
恍飘,點擊Register
,完成APP ID
的注冊谴垫。
? 配置極光推送端需要的兩種證書:開發(fā)證書章母,生產(chǎn)證書汽畴。
- 打開系統(tǒng)里自帶的“鑰匙串訪問”嗡官,創(chuàng)建
CSR
(Certificate Signing Request
)文件 。填寫用戶郵箱和常用名稱察滑,并選擇存儲到磁盤前弯,證書文件后綴為.certSigningRequest
蚪缀。
- 點擊蘋果開發(fā)者網(wǎng)站賬戶左側(cè)的
development/Production
,上傳請求生成的CSR
文件恕出。生成證書后询枚,點擊downLoad
將證書下載到本地中,后綴為.cer
文件浙巫。點擊生成的證書金蜀,在鑰匙串中打開,導出為.p12
文件的畴,并存儲到本地渊抄。
? 把配置好的證書丧裁,傳遞到極光開發(fā)平臺上护桦。
在極光官網(wǎng)申請好的極光推送賬號里創(chuàng)建應用。點擊極光開發(fā)者服務(wù)渣慕,找到推送設(shè)置嘶炭,選擇iOS,用下載好的開發(fā)證書和生產(chǎn)證書導入逊桦,填寫設(shè)置好的p12證書密碼眨猎,點擊保存會生成一個appkey
。集成極光推送SDK
到項目里的時候會用到此appkey
强经。
c睡陪、極光推送的消息形式
通知(APNS):手機的通知欄(狀態(tài)欄)上會顯示的一條通知信息。
自定義消息(應用內(nèi)消息):不會被 SDK 展示到通知欄上。自定義消息主要用于應用的內(nèi)部業(yè)務(wù)邏輯兰迫。朋友圈紅點就可以用這個信殊。極光推送采用的是長連接,所以自定義消息在網(wǎng)絡(luò)正常汁果、App處于前臺的情況下會馬上收到涡拘。
本地通知:SDK集成蘋果實現(xiàn)本地通知。
d据德、極光推送的實現(xiàn)原理
通過我們的App服務(wù)器或極光Web端調(diào)用極光的API能發(fā)起極光推送鳄乏。舉個例子,用戶A
(userIdA
)發(fā)消息給用戶B
(userIdB
)棘利。這里只考慮兩個都綁定好了deviceToken
等橱野,不存在離線消息。
蘋果原生態(tài)下的流程圖
極光下的流程圖
e善玫、JPush APNS通知的意義
iOS平臺上推送通知水援,只有APNS
這個官方的通道,是可以隨時送達的茅郎。一般開發(fā)者都是自己部署應用服務(wù)器向APNS Server
推送蜗元。JPush
推送相比直接向APNS
推送有什么好處呢?
減少開發(fā)及維護成本
- 應用開發(fā)者不需要去開發(fā)維護自己的推送服務(wù)器與
APNS
對接 - 集成了
JPush SDK
后不必自己維護更新device token
- 通過
JPush
的WebPortal
直接推送,也可以調(diào)用JPush
的HTTP
協(xié)議API
來完成只洒,開發(fā)工作量大大減少
減少運營成本
- 極光推送支持一次推送许帐,同時向Android和iOS平臺。支持統(tǒng)一的
API
與推送界面 - 極光推送提供標簽毕谴、別名綁定機制成畦,以及提供了非常細分的用戶分群方式,運營起來非常簡單涝开、直觀
提供應用內(nèi)推送
- 除了使得
APNS
推送更簡單循帐,也另外提供應用內(nèi)消息推送,這在類似于聊天的場景里很有必要
2舀武、項目中極光SDK的配置
a拄养、導入極光SDK
方法1:可以通過CocoaPods
進行導入JPush
。
方法2:手動導入可以參考極光文檔-iOS SDK集成指南银舱。
b瘪匿、進入項目中的appDelegate導入頭文件,遵循代理寻馏。
#import "JPUSHService.h"http:// 引入JPush功能所需頭文件
#import <UserNotifications/UserNotifications.h>// 注冊APNs所需頭文件
@interface JpushManager ()<JPUSHRegisterDelegate,UNUserNotificationCenterDelegate>
@end
c棋弥、在didFinishLaunchingWithOptions中進行JPush的相關(guān)初始化設(shè)置
在didFinishLaunching方法中調(diào)用極光推送的配置方法
- (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為開發(fā)環(huán)境,YES為生產(chǎn)環(huán)境诚欠。虛擬機和真機調(diào)試屬于開發(fā)環(huán)境顽染。測試包漾岳、企業(yè)包和App Store屬于生產(chǎn)環(huán)境
BOOL isProduction = !DEBUG;
BOOL kFUserJPush = NO;
實現(xiàn)極光推送的配置方法
- (void)configureJpushWithLaunchingOption:(NSDictionary *)launchingOption
{
// 初始化推送
[[JpushManager shareManager] setupJPushWithLaunchingOption:launchingOption appKey:JPushAppKey channel:JPushChannel apsForProduction:isProduction advertisingIdentifier:nil];
// 設(shè)置角標為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為設(shè)備廣告標示符,用于廣告投放粉寞。通常不會改變尼荆,不同App獲取到都是一樣的。但如果用戶完全重置系統(tǒng)((設(shè)置程序 -> 通用 -> 還原 -> 還原位置與隱私) 唧垦,這個廣告標示符會重新生成捅儒。
// IDFA用于同一設(shè)備下的不同app信息共享,如不需要使用业崖,advertisingIdentifier 可為nil
// NSString *advertisingId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
[JPUSHService setupWithOption:launchingOption appKey:appKey channel:channel apsForProduction:isProduction advertisingIdentifier:advertisingId];
// 獲取極光推送注冊ID(RegistrationID)
// 原生是采用deviceToken來標識設(shè)備唯一性野芒。在極光中采用RegistrationID
// 其生成原則優(yōu)先采用IDFA(如果設(shè)備未還原IDFA,卸載App后重新下載双炕,還是能被識別出老用戶),次采用deviceToken
// 集成了 JPush SDK 的應用程序在第一次 App 啟動后撮抓,成功注冊到 JPush 服務(wù)器時妇斤,JPush 服務(wù)器會給客戶端返回唯一的該設(shè)備的標識 -—— RegistrationID
[JPUSHService registrationIDCompletionHandler:^(int resCode, NSString *registrationID) {
if(resCode == 0)
{
NSLog(@"registrationID 獲取成功為:%@",registrationID);
// 設(shè)置別名
// 一個設(shè)備只能有一個別名(Alias),但能有多個標簽丹拯。所以別名可以用userId站超,針對一個用戶
// 標簽(Tag)可以用用戶所處分組,方便針對目標用戶推送乖酬,針對一批用戶
[JPUSHService setAlias:[[NSUserDefaults standardUserDefaults] valueForKey:@"userId"] completion:^(NSInteger iResCode, NSString *iAlias, NSInteger seq) {
NSLog(@"設(shè)置別名");
} seq:0];
}
else
{
NSLog(@"registrationID 獲取失敗死相,code為:%d",resCode);
}
}];
}
d、注冊DevieceToken
遠端推送需要獲取設(shè)備的Device Token
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSLog(@"設(shè)備的Device Token為:%@", deviceToken);
// 極光推送注冊 DeviceToken
[[JpushManager shareManager] registerDeviceToken:deviceToken];
}
在appdelegate注冊設(shè)備處調(diào)用極光推送注冊 DeviceToken
- (void)registerDeviceToken:(NSData *)deviceToken
{
[JPUSHService registerDeviceToken:deviceToken];
}
3咬像、JPUSHRegisterDelegate
a算撮、收到通知消息后展示
// 收到推送的消息后的回調(diào)
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);
}
}
// 需要執(zhí)行這個方法,選擇是否提醒用戶县昂,有Badge肮柜、Sound、Banner三種類型可以設(shè)置
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;// 收到推送的消息內(nèi)容
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];
// 點擊消息進行跳轉(zhuǎn)到消息的詳情界面中
// [self goToMssageViewControllerWith:userInfo];
// 系統(tǒng)要求執(zhí)行這個方法
completionHandler();
}
c、點擊通知打開設(shè)置APP
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(UNNotification *)notification
{
if (notification && [notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]])
{
NSLog(@"通知界面進入應用");
}
else
{
NSLog(@"設(shè)置界面進入應用");
}
}
4创淡、封裝的便利方法
a痴晦、設(shè)置角標
- (void)setBadge:(int)badge
{
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge];
[JPUSHService setBadge:badge];
}
b、設(shè)置別名
- (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(@"設(shè)置新的別名: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(@"這條消息價值百萬英鎊!J醴砚尽!")
}
Demo
Demo在我的Github上,歡迎下載辉词。
BasicsDemo