WWDC session - Notifications 學(xué)習(xí)總結(jié)镣煮,如有不妥之處,望請(qǐng)指正????
????最初接觸通知就直接用的三方鄙麦,導(dǎo)致對(duì)于通知的整個(gè)api和它的概念都有一些不甚了解典唇。也只是會(huì)用,能完成正常的需求胯府。
????然后就想著能夠系統(tǒng)的從頭從新了解一些通知介衔,直到最近時(shí)間有所空閑才把從11年開始的session都翻出來看了一遍,順便做了一下整理骂因。
????如果也有像我這種對(duì)通知一知半解的童鞋炎咖,建議不要直接集成三方的推送,可以使用上面的pusher工具,這樣對(duì)于整個(gè)messagePayload的格式都能有個(gè)詳細(xì)的了解乘盼,結(jié)合之后反映到api上學(xué)習(xí)效果更佳急迂。
目錄
一.Notifications 簡介
二.Notifications 結(jié)構(gòu)
三.Push Notifications API
?· 注冊(cè)通知
?· deviceToken是什么
?· Message payload 格式
?· 接收payload
四.Local Notifications
五.iOS9有什么改變
?· new category action - text input (推送消息的快捷回復(fù))
?· text input action 的 payload格式
?· 接收text input category action 響應(yīng)
?· new provider api (新的基于HTTP/2的APNs Protocol)
六.iOS 10 User Notifications
?· UI 變化
?· User Notifications api
???· UNNotificationSound對(duì)象設(shè)置推送聲音
???· 推送的媒體附件
???· 推送觸發(fā)器
???· 推送請(qǐng)求(取消和更新通知)
?· Notifications Delegate
?· Notification Action (可響應(yīng)操作的通知)
?· Notification Service Extension(可變通知擴(kuò)展)
?· Notifications Content Extension(自定義通知UI)
七. Text input action之自定義inputAccessoryView
八.小結(jié)
1.Notifications 簡介
什么是Notifications
那么Notifications到底是什么呢,其實(shí)就是一個(gè)信息彈窗蹦肴,用于反應(yīng)某些事件的僚碎。
為什么要使用Notifications
產(chǎn)品為什么大多都要加上Notifications功能,一方面確實(shí)能在app不處于運(yùn)行狀態(tài)時(shí)也能發(fā)布一些具有時(shí)效性的事件阴幌,另一方面從運(yùn)營方面考慮通知也是一個(gè)app鄙撞活的手段。
推送通知與poll(輪詢)的區(qū)別
push是server驅(qū)動(dòng)矛双,而且是及時(shí)的
poll是app驅(qū)動(dòng)渊抽,而且相對(duì)延時(shí)的
2.Notifications 結(jié)構(gòu)
推送通知又是如何實(shí)現(xiàn)的呢?
推送通知要借助于蘋果的
Apple Push Notifications service
服務(wù)器议忽,簡稱APNs
發(fā)給我們的設(shè)備懒闷。那么
APNs
服務(wù)器怎么知道要發(fā)給哪一臺(tái)設(shè)備呢?這里就由設(shè)備的deviceToken
來標(biāo)識(shí)栈幸。有了
APNs
愤估,有了我們?cè)O(shè)備的deviceToken
,還需要一個(gè)連接我們app和APNs
的provider速址,這里就是我們服務(wù)器了玩焰。如上圖所示,設(shè)備獲取到
deviceToken
芍锚,然后發(fā)送給我們自己的服務(wù)器昔园,服務(wù)器添加payload
json與deviceToken
一起發(fā)送給蘋果的APNs
服務(wù)器,然后由APNs
服務(wù)器將payload
json通知給目標(biāo)設(shè)備并炮。
3.Notifications API
蘋果一直對(duì)于各種權(quán)限要求的比較嚴(yán)格默刚,我們的app既然要使用Notifications的功能,那么就要獲取到用戶的授權(quán)信息逃魄,獲取授權(quán)就要申請(qǐng)注冊(cè)通知荤西。
iOS10之前的通知注冊(cè)由UIApplication
來做。
注冊(cè)通知
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
[application registerUserNotificationSettings:settings];
[application registerForRemoteNotifications];
}else {
[application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound];
}
return YES;
}
注冊(cè)時(shí)可以選用你需要的NotificationTypes
iOS8之后使用新的注冊(cè)通知的方式嗅钻,如果不需要適配iOS7皂冰,則可以拋棄registerForRemoteNotificationTypes
方法。
注冊(cè)通知后的情況
(1). 成功注冊(cè)后會(huì)執(zhí)行該回調(diào)方法养篓,在此方法中可以獲取到deviceToken
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSLog(@"successful--%@",deviceToken);
}
(2). 注冊(cè)失敗后會(huì)執(zhí)行該回調(diào)方法
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(@"did Fail To Register For Remote Notifications With Error: %@", error);
}
注意秃流,模擬器不支持推送通知。
deviceToken是什么
- 它是一個(gè)設(shè)備上某個(gè)app的唯一標(biāo)識(shí)柳弄,有了它才能將消息推送到指定的設(shè)備上的某個(gè)app舶胀。
?·· 它是從UDID中分離出來的- 它是會(huì)發(fā)生改變的(系統(tǒng)升級(jí)概说,app刪除重裝)
?·· 在app每次launch的時(shí)候都要調(diào)用注冊(cè)api確保獲取最新的deviceToken
?·· 不用做deviceToken的緩存
Message payload 格式
注冊(cè)成功,獲取了用戶授權(quán)之后我們需要關(guān)心的內(nèi)容就可以暫時(shí)先放到Message payload
上嚣伐,Message payload
決定了一條推送的內(nèi)容糖赔,聲音,角標(biāo)等等屬性轩端。
Message payload
格式介紹略多放典,并且反應(yīng)到不同iOS版本的字段還有些不同,在這里是大部分通用字段的介紹基茵,在下文中的iOS9和iOS10的介紹里面也會(huì)有針對(duì)版本特性的字段的介紹奋构。
{
"aps" : {
"alert" : {
"body" : "test-body",
"title" : "test-title"
}
"badge": 1,
"sound": "Jingle.aiff"
},
"acme1" : "conversation"
}
Message payload
就是一個(gè)json串,格式如上所示拱层。其中aps
字典包含了聲音弥臼,角標(biāo),內(nèi)容的key-value根灯,aps
字典中所有的key都是可選的径缅。
(1)badge角標(biāo)格式
badge的值為integer,設(shè)置該值之后烙肺,應(yīng)用右上角會(huì)出現(xiàn)數(shù)字角標(biāo)纳猪。
{
"aps" : {
"badge": 1
}
}
清除角標(biāo)數(shù)則可將badge
值設(shè)置為0
{
"aps" : {
"badge": 0
}
}
(2)sound聲音格式
sound的值可以是bundle中的音頻文件名稱。
{
"aps" : {
"sound": "Jingle.aiff"
}
}
如果使用"default"茬高,則接收到推送時(shí)為系統(tǒng)默認(rèn)聲音兆旬。
{
"aps" : {
"sound": "default"
}
}
其中接收到推送的震動(dòng)是默認(rèn)自帶的,不需要使用鍵值控制怎栽。
(3)alert內(nèi)容格式
alert的值蘋果推薦使用字典來配置,其可用的key 有
key | desc | type | version |
---|---|---|---|
title |
推送的標(biāo)題 | String |
8.2 |
body |
推送的內(nèi)容 | String |
|
title-loc-key |
本地化推送標(biāo)題的key,可以使用%@ 和 %n$@ 格式化配置從title-loc-args 數(shù)組中獲取變量值 |
String or null
|
8.2 |
title-loc-args |
本地化推送標(biāo)題key對(duì)應(yīng)的變量值數(shù)組 | 字符串?dāng)?shù)組 or null
|
8.2 |
action-loc-key |
action 按鈕標(biāo)題本地化配置的key |
String or null
|
- |
loc-key |
本地化消息的key宿饱,可以使用%@ 和 %n$@ 格式化配置從loc-args 數(shù)組中獲取變量值 |
String |
- |
loc-args |
本地化消息key對(duì)應(yīng)的變量值數(shù)組 | 字符串?dāng)?shù)組 | - |
launch-image |
bundle中的一個(gè)圖片熏瞄,可以有圖片的后綴名,也可以沒有谬以。<br />?如果設(shè)置了這個(gè)鍵值强饮,那么用戶點(diǎn)擊推送視圖打開app時(shí),LaunchImage 就會(huì)被指定為該圖片为黎。<br />?如果沒有指定該值邮丰,則仍然使用app默認(rèn)在info.plist 中使用UILaunchImageFile 配置的圖片。 |
String |
- |
(4)推送本地化
為了有針對(duì)性的對(duì)不同地區(qū)铭乾,不同語言做推送的本地化剪廉,可以使用alert
中的一些本地化key。
推送的本地化有兩種方式:
A - 服務(wù)器提供--需要將用戶設(shè)備當(dāng)前的語言設(shè)置傳遞給服務(wù)器炕檩。
當(dāng)前設(shè)備的語言偏好設(shè)置獲取可以使用 NSLocale
的preferredLanguages
屬性來獲取斗蒋。
NSString *preferredLang = [[NSLocale preferredLanguages] objectAtIndex:0];
const char *langStr = [preferredLang UTF8String];
需要注意的是:用戶可以修改語言的系統(tǒng)偏好設(shè)置,這樣就要監(jiān)聽語言改變的NSCurrentLocaleDidChangeNotification
通知了,在系統(tǒng)語言發(fā)生變化時(shí)上報(bào)給服務(wù)器泉沾。
這樣的好處是服務(wù)器想推什么推什么捞蚂。
B - 使用Localizable.strings
文件配置--需要將本地化的消息事先配置好,靈活性相較于服務(wù)器提供有所欠缺跷究。
Localizable.strings
中配置類似如下的鍵值對(duì):
"GAME_PLAY_REQUEST_FORMAT" = "%@ and %@ have invited you to play Monopoly";
Message payload
中alert的格式如下:
{
"aps" : {
"alert" : {
"loc-key" : "GAME_PLAY_REQUEST_FORMAT",
"loc-args" : [ "Jenna", "Frank"]
}
}
}
這樣就可以在app中做推送的本地化配置了姓迅。
接收payload
- 如果你的app在運(yùn)行中,你只能通過以下方法獲取俊马。
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
NSLog(@"推送消息===%@",userInfo);
//處理傳過來的推送消息
}
- 如果你的app不在運(yùn)行狀態(tài)队贱,當(dāng)點(diǎn)擊彈窗視圖時(shí),只能通過以下方法獲取到通知的payload潭袱。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
...
return YES;
}
可以通過UIApplicationLaunchOptionsRemoteNotificationKey
從launchOptions
中獲取到payload柱嫌。
注意點(diǎn)
需要注意這個(gè)fetchCompletionHandler
方法
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler NS_AVAILABLE_IOS(7_0);
如果實(shí)現(xiàn)了這個(gè)方法,那么- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
方法將不會(huì)被執(zhí)行屯换。
didReceiveRemoteNotification:fetchCompletionHandler:
方法有什么作用呢编丘?
按照蘋果官方的解釋這個(gè)代理方法,為開啟了remote-notification
background mode的app提供了一個(gè)機(jī)會(huì)去獲取適當(dāng)?shù)男聰?shù)據(jù)彤悔,來響應(yīng)即將到來的遠(yuǎn)程通知嘉抓。
也就是說蘋果給了一個(gè)在程序在后臺(tái)運(yùn)行時(shí)能繼續(xù)跑代碼的方法。(注意程序需要在后臺(tái)運(yùn)行中)晕窑。
4.Local Notifications
Local Notification 和 Push Notification有什么區(qū)別呢抑片?
Push Notification是由服務(wù)器發(fā)出的,Local Notification是由app發(fā)出的杨赤。
Push Notification是一次性的敞斋,Local Notification則是可以事先設(shè)定的,而且是可重復(fù)的疾牲。
如果你要實(shí)現(xiàn)一個(gè)鬧鈴的提醒或者是一個(gè)備忘提醒植捎,那么就非常適合使用Local Notification來實(shí)現(xiàn)了。
Local Notification API
- Badge (角標(biāo))
NSInteger applicationIconBadgeNumber
- Alerts
NSString *alertBody 通知內(nèi)容
NSString *alertTitle 標(biāo)題 // 8.2
NSString *category // 8.0
BOOL hasAction
NSString *alertAction
NSString *alertLaunchImage 自定義LaunchImage
- Sound (聲音)
NSString *soundName 推送聲音
- Scheduling (設(shè)定)
NSDate *fireDate 推送時(shí)間
NSTimeZone *timeZone 時(shí)區(qū)
- Repeating (重復(fù)設(shè)置)
NSCalendarUnit repeatInterval
NSCalendar *repeatCalendar
- Metadata
NSDictionary *userInfo 推送payload
直接上代碼演示
UILocalNotification *note = [[UILocalNotification alloc] init];
note.applicationIconBadgeNumber = 3; // 角標(biāo)
note.alertBody = @"test body"; // 內(nèi)容
note.alertTitle = @"test title"; // 標(biāo)題
note.soundName = @"test.aiff"; // 自定義推送聲音
// note.soundName = UILocalNotificationDefaultSoundName; // 默認(rèn)聲音
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *dateComos = [[NSDateComponents alloc] init];
[dateComos setDay:10];
[dateComos setMonth:6];
[dateComos setYear:2017];
[dateComos setHour:11];
note.fireDate = [calendar dateFromComponents:dateComos]; // 推送發(fā)出的時(shí)間
// note.fireDate = [NSDate dateWithTimeIntervalSinceNow:5.0];
note.timeZone = [calendar timeZone]; // 時(shí)區(qū)
note.repeatInterval = NSCalendarUnitDay; // 每天這個(gè)時(shí)間重復(fù)發(fā)出
/*
常用的key如下:
NSCalendarUnitEra ,
NSCalendarUnitYear ,
NSCalendarUnitMonth ,
NSCalendarUnitDay ,
NSCalendarUnitHour ,
NSCalendarUnitMinute ,
NSCalendarUnitSecond ,
NSCalendarUnitWeekday ,
NSCalendarUnitWeekdayOrdinal ,
NSCalendarUnitQuarter ,
NSCalendarUnitWeekOfMonth ,
NSCalendarUnitWeekOfYear ,
NSCalendarUnitYearForWeekOfYear ,
NSCalendarUnitNanosecond ,
NSCalendarUnitCalendar ,
NSCalendarUnitTimeZone
*/
note.repeatCalendar = [NSCalendar currentCalendar];
// 使用scheduleLocalNotification方法可以在指定的fireDate發(fā)送本地通知
[[UIApplication sharedApplication] scheduleLocalNotification:note];
// 使用presentLocalNotificationNow方法則會(huì)忽略fireDate直接發(fā)送該通知
//[[UIApplication sharedApplication] presentLocalNotificationNow:note];
// 當(dāng)然可以使用cancelLocalNotification取消掉某個(gè)通知的發(fā)布阳柔,如果該通知已經(jīng)彈出焰枢,調(diào)用該方法也會(huì)dismiss該通知。
//[[UIApplication sharedApplication] cancelLocalNotification:note];
接收本地推送
如果app在運(yùn)行舌剂,則會(huì)執(zhí)行下面的方法
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
NSLog(@"%@",notification.userInfo);
}
如果app不在運(yùn)行济锄,則可以在launchOptions
中通過UIApplicationLaunchOptionsLocalNotificationKey
獲取到本地通知
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *note = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
return YES;
}
5.iOS9有什么改變
new category action - text input (推送消息的快捷回復(fù))
iOS9新添加了一個(gè)UIUserNotificationAction
的type -> UIUserNotificationActionBehaviorTextInput
在注冊(cè)通知setting的時(shí)候可以添加此UIUserNotificationAction
,來實(shí)現(xiàn)通知消息的快捷回復(fù)霍转,如下圖:
示例代碼如下:
// 聲明一個(gè)操作
UIMutableUserNotificationAction *action = [[UIMutableUserNotificationAction alloc] init];
action.title = @"回復(fù)";
action.identifier = @"test-replay-action";
action.behavior = UIUserNotificationActionBehaviorTextInput;
// 聲明一個(gè)操作分類
UIMutableUserNotificationCategory *category = [[UIMutableUserNotificationCategory alloc] init];
category.identifier = @"test-replay"; // 注冊(cè)操作分類的identifier
[category setActions:@[action] forContext:UIUserNotificationActionContextDefault];
NSSet *set = [NSSet setWithObjects:category, nil];
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:set];
[application registerUserNotificationSettings:settings];
[application registerForRemoteNotifications];
如代碼中所示荐绝,需要首先聲明一個(gè)UIUserNotificationActionBehaviorTextInput
類型的UIUserNotificationAction
。
然后將其添加到已經(jīng)注冊(cè)了identifier
的操作分類中谴忧,然后在UIUserNotificationSettings
設(shè)置此分類很泊。
調(diào)用application
的注冊(cè)方法角虫,將UIUserNotificationSettings
配置進(jìn)去,至此將UIUserNotificationActionBehaviorTextInput
快捷回復(fù)的操作注冊(cè)完畢委造。
text input action 的 payload格式
之后在Message payload中添加category
字段戳鹅,category
字段的value值為之前注冊(cè)的操作分類的identifier,即category.identifier
昏兆。
{
aps = {
alert = {
body = "test action";
title = "test action title";
};
badge = 1;
category = "test-replay";
sound = default;
}
接收text input category action 響應(yīng)
push notifications可以在下面這個(gè)方法中接收輸入操作
- (void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void(^)())completionHandler {
NSLog(@"identifier---%@---userInfo---%@---responseInfo---%@",identifier,userInfo,responseInfo);
}
local notifications 可以在這個(gè)方法中接收輸入操作
- (void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void(^)())completionHandler {
NSLog(@"identifier---%@---notification---%@---responseInfo---%@",identifier,notification,responseInfo);
}
由identifier
字段來區(qū)分不同的action
log輸出結(jié)果如下:
identifier---test-replay-action---userInfo---{
aps = {
alert = {
body = "test action";
title = "test action title";
};
badge = 1;
category = "test-replay";
sound = default;
};
}---responseInfo---{
UIUserNotificationActionResponseTypedTextKey = "\U54c8\U54c8\U54c8\U54c8\U54c8";
}
new provider api
在iOS9中蘋果升級(jí)了APNs push Protocol枫虏,這個(gè)新版本的協(xié)議基于HTTP/2和JSON,相比于舊的二進(jìn)制協(xié)議爬虱,新的協(xié)議有了巨大改進(jìn)隶债。
新的provider api在前端開發(fā)中涉及不多,在這里就不再細(xì)說跑筝,有興趣的可以點(diǎn)擊以下鏈接進(jìn)行細(xì)節(jié)研究死讹。
官方Binary Provider API
WWDC session 720
6.iOS 10 User Notifications
UI 變化
在iOS10中最直觀的改變就是UI的改變,一個(gè)通知中包含了標(biāo)題曲梗,子標(biāo)題赞警,內(nèi)容,以及媒體附件虏两。如下圖:
User Notifications api
在iOS10愧旦,蘋果將Notifications進(jìn)行了重構(gòu)。
從iOS10開始UINotification
已全部被標(biāo)記為廢棄定罢,如果你的app不需要支持更早的版本笤虫,你就可以使用最新的User Notifications Framework
了。
直接導(dǎo)入#import <UserNotifications/UserNotifications.h>
即可使用祖凫。
與之前的api相比較琼蚯,UN框架將通知的初始化與發(fā)送做了更加細(xì)化的重構(gòu)。
之前幾乎所有的內(nèi)容蝙场,觸發(fā)凌停,是否重復(fù) 等等屬性全部都在UINotification
中設(shè)置。
UN框架則將其細(xì)化為大致如下內(nèi)容:
?- 新的注冊(cè)api
?- 通知的內(nèi)容 UNNotificationContent
售滤,包含推送內(nèi)容的一些基本屬性設(shè)置
?- 通知觸發(fā)器 UNNotificationTrigger
,分為
????· 推送觸發(fā)器UNPushNotificationTrigger
????· 時(shí)間觸發(fā)器UNTimeIntervalNotificationTrigger
????· 日期觸發(fā)器UNCalendarNotificationTrigger
????· 以及位置觸發(fā)器UNLocationNotificationTrigger
?- 通知請(qǐng)求 UNNotificationRequest
台诗,請(qǐng)求中包含通知內(nèi)容以及通知觸發(fā)器完箩。
?- 最后將通知請(qǐng)求添加到推送中心,交由通知中心調(diào)度拉队。
示例代碼如下:
// 注冊(cè)
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
}];
// 聲明一個(gè)通知content
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = @"hello world";
content.subtitle = @"test notifications";
content.body = @"hello body";
UNNotificationSound *sound = [UNNotificationSound defaultSound];
content.sound = sound;
// 初始化一個(gè)圖片附件
NSString *picAttachMentIdentifier = @"picAttachMentIdentifier";
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"IMG_3836" ofType:@"JPG"]];
NSError *error ;
UNNotificationAttachment *picAttachMent = [UNNotificationAttachment attachmentWithIdentifier:picAttachMentIdentifier URL:url options:nil error:&error];
/*注意如果無法獲取到file url 的data弊知,UNNotificationAttachment則會(huì)返回nil*/
content.attachments = @[picAttachMent];
// 聲明一個(gè)時(shí)間觸發(fā)器
UNTimeIntervalNotificationTrigger *timerTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5.0 repeats:NO];
// 聲明一個(gè)通知請(qǐng)求
NSString *requestIdentifier = @"requestIdentifier";
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:requestIdentifier content:content trigger:timerTrigger];
// 將通知請(qǐng)求交給推送中心調(diào)度,通知中心會(huì)在合適時(shí)機(jī)發(fā)布該通知粱快。
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
}];
由上述調(diào)用代碼可以看出來秩彤,這次的重構(gòu)將之前在幾乎都是在一個(gè)類中配置的各種屬性基本都給分離出來了叔扼。
通知payload相關(guān)的基本都在UNMutableNotificationContent
類中,包括:
attachments // 媒體附件
badge // 角標(biāo)數(shù)值
body // 內(nèi)容
subtitle // 子標(biāo)題
title // 標(biāo)題
categoryIdentifier // 操作分類id
launchImageName // 推送喚醒時(shí)的launchImage 圖片
sound // 聲音
userInfo // 額外附帶信息
threadIdentifier // 通知request 的線程id
sound 推送聲音
推送聲音的設(shè)置現(xiàn)在不在是一個(gè)字符串了漫雷,需要給content傳遞一個(gè)UNNotificationSound
對(duì)象瓜富。
// 默認(rèn)推送聲音
UNNotificationSound *sound = [UNNotificationSound defaultSound];
content.sound = sound;
// 自定義推送音效
// UNNotificationSound *sound = [UNNotificationSound soundNamed:@"sms-received1.caf"];
如果創(chuàng)建本地推送時(shí),不給content設(shè)置sound屬性的值降盹,則推送默認(rèn)沒有聲音与柑。
attachments 媒體附件
媒體附件支持的格式以及大小如下圖所示:
如果為不支持的文件類型,或者大小超過了蓄坏。則返回空
attachments
對(duì)象价捧。
Trigger觸發(fā)器
通知的發(fā)送需要給這條通知設(shè)置相應(yīng)的觸發(fā)器,iOS10之后蘋果提供了以下的觸發(fā)器:
UNTimeIntervalNotificationTrigger
時(shí)間觸發(fā)器涡戳,該觸發(fā)器可以設(shè)置通知什么時(shí)候發(fā)出结蟋,是否重復(fù)發(fā)送。
UNTimeIntervalNotificationTrigger *timerTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5.0 repeats:NO];
UNCalendarNotificationTrigger
日期觸發(fā)器可以設(shè)置具體的日期的通知提醒渔彰。
NSDateComponents *dateComos = [[NSDateComponents alloc] init];
[dateComos setDay:10];
[dateComos setMonth:6];
[dateComos setYear:2017];
[dateComos setHour:11];
UNCalendarNotificationTrigger *calendarTrigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:dateComos repeats:YES];
UNLocationNotificationTrigger
地點(diǎn)觸發(fā)器可以在用戶進(jìn)入某個(gè)區(qū)域時(shí)給用戶通知提醒嵌屎。
CLRegion *region ;
UNLocationNotificationTrigger *locationTrigger = [UNLocationNotificationTrigger triggerWithRegion:region repeats:YES];
推送請(qǐng)求(取消和更新通知)
一個(gè)UNNotificationRequest
對(duì)象中包含了一條推送的內(nèi)容和觸發(fā)器,將推送請(qǐng)求對(duì)象交給通知中心之后胳岂,這條通知才會(huì)在通知中心的調(diào)度下在合適的觸發(fā)時(shí)機(jī)下發(fā)出编整。
而UNNotificationRequest
的作用又是什么呢?
在iOS10中乳丰,可以通過UNNotificationRequest
來取消或者更新通知掌测。而這個(gè)取消和更新的關(guān)鍵就在于UNNotificationRequest
的requestIdentifier
屬性。
取消未發(fā)出的通知可以使用以下方法:
[[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[@"com.wkj.requestIdentifie"]];
取消已發(fā)出的通知可以使用以下方法:
[[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:@[requestIdentifier]];
更新未發(fā)出的通知可以使用以下方法:
UNTimeIntervalNotificationTrigger *newTimerTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:20.0 repeats:NO];
UNNotificationRequest *newRequest = [UNNotificationRequest requestWithIdentifier:requestIdentifier content:content trigger:newTimerTrigger];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:newRequest withCompletionHandler:^(NSError * _Nullable error) {
}];
經(jīng)測試通過requestIdentifier
更新通知的方式分兩種情況:
- 對(duì)于未發(fā)出的推送产园,只能更新通知的觸發(fā)器汞斧,如果重新設(shè)置了
request
的content
。則原通知的content不會(huì)進(jìn)行更新什燕,且新的觸發(fā)器失效粘勒。 -
對(duì)于已發(fā)出的推送,可以重新設(shè)置觸發(fā)器和內(nèi)容屎即。如下圖:
222.gif
神奇小貼士
?注意UNNotificationRequest
對(duì)象的requestIdentifier
屬性庙睡,不能設(shè)置為@"",貌似會(huì)變磚技俐,有多余設(shè)備的同志試驗(yàn)后請(qǐng)告知結(jié)果( ′???)σ乘陪。
Notifications Delegate
對(duì)比之前的注冊(cè)方法,iOS10之前雕擂,使用UIApplication
進(jìn)行注冊(cè)操作啡邑,默認(rèn)通知的代理回調(diào)需要在AppDelegate
中處理。
而iOS10則需要自己來設(shè)置代理井赌,可以在注冊(cè)結(jié)果回調(diào)的block中根據(jù)回調(diào)結(jié)果谤逼,做代理的設(shè)置贵扰。
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];
}
}];
靈活的設(shè)置代理方法,可以將通知的代理方法從AppDelegate
剝離出去流部。
UNUserNotificationCenterDelegate的兩個(gè)代理方法
- 處于前臺(tái)時(shí)的代理回調(diào)方法
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
UNNotificationPresentationOptions presentationOptions = UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge;
completionHandler(presentationOptions);
}
當(dāng)app處于前臺(tái)時(shí)戚绕,會(huì)執(zhí)行該方法,在此方法中可以過濾將要顯示的通知的一些設(shè)置選項(xiàng)贵涵,例如如果處于前臺(tái)時(shí)收到通知列肢,將通知的角標(biāo)設(shè)置選項(xiàng)給過濾掉。
如果想要在前臺(tái)時(shí)宾茂,顯示通知的alert彈框則需要注意瓷马,一定要執(zhí)行completionHandler()
。
不執(zhí)行completionHandler()
的話是不會(huì)在前臺(tái)時(shí)顯示通知的alert彈框的跨晴。
神奇小貼士:
?如果沒有實(shí)現(xiàn)該方法欧聘,仍然想要通知在前臺(tái)顯示,則可以設(shè)置UNNotificationContent
的shouldAlwaysAlertWhileAppIsForeground
屬性端盆。
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
[content setValue:@YES forKey:@"shouldAlwaysAlertWhileAppIsForeground"];
- 處于后臺(tái)時(shí)的代理回調(diào)方法
當(dāng)app處于運(yùn)行狀態(tài)時(shí)怀骤,不管是本地還是遠(yuǎn)程通知,當(dāng)用戶點(diǎn)擊推送的alert彈窗時(shí)焕妙,則會(huì)執(zhí)行該方法蒋伦。
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
?????didReceiveNotificationResponse:(UNNotificationResponse *)response
?????withCompletionHandler:(void(^)())completionHandler {
NSLog(@"notification response : %@",response);
}
當(dāng)app不在運(yùn)行狀態(tài)時(shí),仍然只能在application:didFinishLaunchingWithOptions:
中獲取到通知內(nèi)容焚鹊。
推送通知獲群劢臁:
NSDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
本地通知獲取:
NSDictionary *userInfoLocal = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
神奇小貼士:
?UIApplicationLaunchOptionsLocalNotificationKey
在iOS10中被標(biāo)記為廢棄狀態(tài)末患,被建議使用userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:
方法替代研叫。
但是當(dāng)app不在運(yùn)行狀態(tài)時(shí),此方法是不會(huì)被執(zhí)行的璧针,如果想要在app不在運(yùn)行狀態(tài)時(shí)嚷炉,仍然響應(yīng)本地通知相關(guān)事件的話,還是只能使用UIApplicationLaunchOptionsLocalNotificationKey
獲取探橱。
Notification Action (可響應(yīng)操作的通知)
在上文中介紹過iOS9的action申屹,與之前的操作相類似,自定義通知的
Action
需要實(shí)現(xiàn)注冊(cè)隧膏,套路與之前版本的也類似独柑。
在iOS10里面,Action
分兩種私植,一種是UNNotificationAction
,另外一種是UNTextInputNotificationAction
车酣。示例代碼如下:
// 默認(rèn)action
UNNotificationAction *action = [UNNotificationAction actionWithIdentifier:@"iOS10-刪除" title:@"刪除" options:UNNotificationActionOptionDestructive];
// 輸入框action
UNTextInputNotificationAction *textInputNotificationAction = [UNTextInputNotificationAction actionWithIdentifier:@"iOS10-replay" title:@"回復(fù)" options:UNNotificationActionOptionAuthenticationRequired textInputButtonTitle:@"test" textInputPlaceholder:@"placeholder"];
UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:@"iOS10-category-identifier" actions:@[action , textInputNotificationAction] intentIdentifiers:nil options:UNNotificationCategoryOptionCustomDismissAction];
NSSet *set = [NSSet setWithObject:category];
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:set];
使用也與之前的類似曲稼,遠(yuǎn)程推送在payload中添加category
字段索绪,value值為category初始化時(shí)填寫的identifier
。
本地推送同樣給content設(shè)置categoryIdentifier
:
content.categoryIdentifier = @"iOS10-category-identifier";
其中UNTextInputNotificationAction
初始化參數(shù)中的textInputButtonTitle
為輸入框右側(cè)操作按鈕的標(biāo)題贫悄,textInputPlaceholder
參數(shù)為輸入框的占位提示文字瑞驱。效果如下圖所示:
接收action的響應(yīng)操作
action的操作響應(yīng)可以在下面這個(gè)方法的UNNotificationResponse
中獲取
?- (void)userNotificationCenter:(UNUserNotificationCenter *)center ?didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler {
? completionHandler();
?}
UNNotificationResponse
結(jié)構(gòu)如下圖所示:
如果是
UNTextInputNotificationAction
的響應(yīng),則返回的response對(duì)象類型為UNTextInputNotificationResponse
窄坦,用戶輸入的內(nèi)容可由UNTextInputNotificationResponse
的userText
屬性獲取唤反。
Notification Service Extension(可變通知擴(kuò)展)
Notification Service Extension是iOS10新增的一個(gè)Extension,用于增加或者替換遠(yuǎn)程推送內(nèi)容的鸭津。
反映到實(shí)際開發(fā)上:
-
Notification Service
可以解決推送敏感內(nèi)容的端到端加密(End-to-end encryption
) - 也可以給遠(yuǎn)程推送添加本地的媒體文件
如何使用Notification Service Extension實(shí)現(xiàn)修改推送內(nèi)容彤侍,添加本地媒體文件
Advanced Notifications 之Notifications Content Extension(自定義通知UI)
除了使用系統(tǒng)默認(rèn)的Notification's UI,蘋果還提供了Notifications Content Extension
方便開發(fā)者進(jìn)行UI的自定義逆趋。如下圖所示:
如何使用Notification Content Extension實(shí)現(xiàn)自定義推送UI
Text input action之自定義inputAccessoryView
系統(tǒng)默認(rèn)的Text input action只有一個(gè)輸入框盏阶,一個(gè)右側(cè)的按鈕。如果想要修改通知Text input action喚起的inputAccessoryView
怎么辦呢闻书。
很簡單名斟,這里并不會(huì)用到新的api。
- 1砰盐、重寫canBecomeFirstResponder方法
-(BOOL)canBecomeFirstResponder{
return YES;
}
- 2、重寫inputAccessoryView的getter方法返回自定義的inputAccessoryView
-(UIView *)inputAccessoryView{
return customInputView;
}
之后記得在下面這個(gè)方法中這么用:
- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption option))completion{
if ([response.actionIdentifier isEqualToString:@"iOS10-replay"] ) {
[self becomeFirstResponder];
[self.textFiled becomeFirstResponder];
}
completion(UNNotificationContentExtensionResponseOptionDoNotDismiss);
}
效果如下圖:
小結(jié)
從最初的push一路看下來發(fā)現(xiàn)岩梳,蘋果幾乎每年都會(huì)添加一些新的feature脾歇。每次改動(dòng)不多,而具體怎么利用這些特性池摧,就靠開發(fā)者各顯神通了。
為了最大程度的保持app用戶的活躍作彤,我們最常用的方式就是經(jīng)由APNs服務(wù)器發(fā)送remote push乌逐。
對(duì)于一些偏旅游推薦竭讳,景區(qū)介紹類的app,利用好地點(diǎn)觸發(fā)器直接進(jìn)行針對(duì)性推薦也是非常提升用戶體驗(yàn)的浙踢。
除了這些绢慢,還有iOS8之后提供的PushKit,對(duì)于開啟了voip通道的IM應(yīng)用來說洛波,直接使用PushKit喚醒胰舆,拉取離線消息骚露,生成local push的方式對(duì)其體驗(yàn)的提升也是非常大的。
參考鏈接:
session 707 Introduction to Notifications--2016
session 708 Advanced Notifications--2016
session 724--2016
session 720--2015
session 517--2011