iOS10發(fā)布以來(lái)捂掰,相信各位開(kāi)發(fā)者能踩的坑也應(yīng)該踩得差不多了;但也許正是因?yàn)槊看翁O(píng)果都會(huì)更新比較多的東西,才會(huì)覺(jué)得搞iOS很有意思吧袖瞻。(不知道大家會(huì)不會(huì)覺(jué)得樓主這種想法有點(diǎn)坑?)
推送是iOS10系統(tǒng)變動(dòng)比較大的一個(gè)地方,對(duì)于這種大變動(dòng)不瞅瞅一下著實(shí)不符合樓主的性格拆吆,那么樓主就在求知欲的推動(dòng)下(畢竟還得給自己的項(xiàng)目進(jìn)行適配不是..)就花了點(diǎn)時(shí)間對(duì)UserNotifications這個(gè)框架進(jìn)行了一個(gè)整體的了解聋迎,希望在這里記錄一下,希望能夠幫助對(duì)這個(gè)框架有興趣的小伙伴枣耀。
如果大家的項(xiàng)目中推送是使用了極光霉晕、友盟等第三方推送,那么請(qǐng)根據(jù)他們的官方文檔以及最新的SDK對(duì)iOS10進(jìn)行適配即可捞奕,跟著文檔一步一步走也比較簡(jiǎn)單牺堰。但是如果想要真實(shí)的了解原框架的流程,建議還是使用原生推送吧颅围,樓主本文博客下使用的就是原生推送伟葫。
如何配置原生推送,推薦有夢(mèng)想的蝸牛 一步一步教你做ios推送院促,這位博主寫(xiě)的很詳細(xì)筏养,雖然開(kāi)發(fā)者界面已經(jīng)不是那樣子了,但實(shí)質(zhì)的東西還是沒(méi)有變化的常拓。如果按照推薦博文的配置(!一定要確認(rèn)所有的配置都沒(méi)有問(wèn)題!)渐溶,但依舊收不到遠(yuǎn)程推送,那么估計(jì)就和樓主一樣“中獎(jiǎng)”了弄抬,可能是PHP端交互出問(wèn)題了茎辐,這個(gè)時(shí)候推薦大家使用PushMeBody來(lái)完成遠(yuǎn)程推送。(使用PushMesBody中的device token直接復(fù)制打印的device token即可,不需要去掉空格)
為了能夠區(qū)分它與之前用法的異同拖陆,樓主還是盡可能的想通過(guò)比較的方法實(shí)現(xiàn)推送的相關(guān)功能弛槐。
所有coding都在 RITL/RITLPushNoticationDemo(如果有幫助,請(qǐng)Star表示支持一下慕蔚,感謝)丐黄。
如果有什么問(wèn)題,也請(qǐng)及時(shí)指出孔飒,共同進(jìn)步灌闺,感謝。
推送的注冊(cè)
注冊(cè)的位置沒(méi)有任何的變化坏瞄,是在Appdelegate中的-application: didFinishLaunchingWithOptions:
方法里面進(jìn)行注冊(cè)桂对,樓主為了簡(jiǎn)化它的代碼,將注冊(cè)功能封裝成了一個(gè)RITLNotificationManager
類(lèi)鸠匀,所以該方法下只需一句話即可:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//初始化個(gè)數(shù)
[UIApplication sharedApplication].applicationIconBadgeNumber = 0;
//注冊(cè)所有的推送
[[RITLNotificationManager sharedInstance]registerRemoteNotificationsApplication:self];
return YES;
}
注冊(cè)具體代碼實(shí)現(xiàn)
RITLNotificationManager的聲明(樓主在聲明文件中使用了預(yù)編譯進(jìn)行方法的聲明蕉斜,實(shí)際的開(kāi)發(fā)中建議不要這么寫(xiě),將方法分開(kāi)便是缀棍,不同的版本使用不同的方法即可)以及實(shí)現(xiàn)方法如下:
//RITLNotificationManager.h(實(shí)際Demo中它在"RITLOriginPushAppDelegate+RITLNotificationManager.h"文件下)
/// 注冊(cè)遠(yuǎn)程推送
#ifdef __IPHONE_10_0
- (void)registerRemoteNotificationsApplication:(id<UNUserNotificationCenterDelegate>)application;
#else
- (void)registerRemoteNotificationsApplication:(id)application;
#endif
//RITLOriginPushAppDelegate+RITLNotificationManager.m
#ifdef __IPHONE_10_0
-(void)registerRemoteNotificationsApplication:(id<UNUserNotificationCenterDelegate>)application
#else
- (void)registerRemoteNotificationsApplication:(id)application
#endif
{
#ifdef __IPHONE_10_0
//設(shè)置代理對(duì)象
[UNUserNotificationCenter currentNotificationCenter].delegate = application;
// 請(qǐng)求權(quán)限
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:UNAuthorizationOptionBadge|UNAuthorizationOptionSound|UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted == true)//如果準(zhǔn)許宅此,注冊(cè)推送
{
[[UIApplication sharedApplication]registerForRemoteNotifications];
}
}];
#else
#ifdef __IPHONE_8_0 //適配iOS 8
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert categories:nil]];
[[UIApplication sharedApplication]registerForRemoteNotifications];
#else //適配iOS7
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert];
#endif
#endif
}
注冊(cè)完畢進(jìn)行的回調(diào)
這一部分iOS10沒(méi)有發(fā)生變化,還是和之前一樣爬范,分為注冊(cè)成功以及注冊(cè)失敗兩個(gè)協(xié)議方法父腕,方法就在類(lèi)別RITLOriginPushAppDelegate+RITLNotificationManager
:
// 注冊(cè)推送成功
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
// NSLog(@"token = %@",deviceToken);
// 將返回的token發(fā)送給服務(wù)器
}
// 注冊(cè)推送失敗
-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
NSLog(@"remoteNotice failture = %@",error.localizedDescription);
}
UserNotifications以前接收推送消息
在Demo中對(duì)AppDelegate新建了一個(gè)類(lèi)別"RITLOriginPushAppDelegate+RITLOldNotification"
來(lái)完成對(duì)之前推送消息的接收以及處理:
//通過(guò)點(diǎn)擊遠(yuǎn)程推送進(jìn)入App執(zhí)行的方法,不管是應(yīng)用被殺死還是位于后臺(tái)
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
//收到的信息
NSLog(@"%@",[[userInfo valueForKey:@"aps"] valueForKey:@"alert"]);
[self performSelector:NSSelectorFromString(@"__showAlert:") withObject:@"通過(guò)點(diǎn)擊推送進(jìn)入App" afterDelay:0];
}
// 在前臺(tái)收到遠(yuǎn)程推送執(zhí)行的方法,如果實(shí)現(xiàn)了iOS10的協(xié)議方法,該方法不執(zhí)行,雖然沒(méi)有標(biāo)明廢棄
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler
{
//這個(gè)時(shí)候不會(huì)彈出推送青瀑,通常璧亮,在此處進(jìn)行一次本地推送,進(jìn)行通知
//coding..
//回調(diào)
completionHandler(UIBackgroundFetchResultNewData);
}
// 收到本地推送
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
//coding to handle local notification
NSLog(@"本地推送啦!");
//獲得文本與詳細(xì)內(nèi)容
NSString * content = [NSString stringWithFormat:@"本地通知:title = %@, subTitle = %@",notification.alertBody,notification.alertTitle];
[self performSelector:NSSelectorFromString(@"__showAlert:") withObject:content afterDelay:1];
}
UserNotifications下的接收推送消息
由于UserNotifications不再對(duì)遠(yuǎn)程推送以及本地推送進(jìn)行區(qū)分斥难,所以只需實(shí)現(xiàn)UNUserNotificationCenterDelegate
協(xié)議下的兩個(gè)協(xié)議方法即可枝嘶,樓主是將該方法寫(xiě)在了AppDelegate的另一類(lèi)別中RITLOriginPushAppDelegate+RITLUserNotifications
:
#pragma mark - <UNUserNotificationCenterDelegate>
// 在前臺(tái)收到通知時(shí),將要彈出通知的時(shí)候觸發(fā)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
//比如如果App實(shí)在前臺(tái)哑诊,就不需要Badge了
if([UIApplication sharedApplication].applicationState == UIApplicationStateActive)
{
completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
}
else//如果是后臺(tái)或者不活躍狀態(tài)群扶,需要badge
{
//需要三種彈出形式,如果存在Alert,那么App在前臺(tái)也是可以從上面彈出的
completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
}
}
// 已經(jīng)收到通知響應(yīng)的處理方法,不管是什么通知镀裤,當(dāng)通過(guò)點(diǎn)擊推送進(jìn)入或者回到App的時(shí)候觸發(fā)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler
{
//獲得響應(yīng)對(duì)象
UNNotification * notification = response.notification;
//獲得響應(yīng)時(shí)間
// NSDate * responseDate = notification.date;
//獲得響應(yīng)體
UNNotificationRequest * request = notification.request;
//獲得響應(yīng)體的標(biāo)識(shí)符
// NSString * identifier = request.identifier;
// 喚起通知的對(duì)象
// UNNotificationTrigger * trigger = request.trigger;
//獲得通知內(nèi)容
UNNotificationContent * content = request.content;
//比如獲得我想要的alert
NSString * alertString = content.body;
//可以彈出Alert提示一下
[self performSelector:NSSelectorFromString(@"__showAlert:") withObject:alertString afterDelay:1];
//比如這里可以進(jìn)行界面的跳轉(zhuǎn)等操作...
//告知完成
completionHandler();
}
遠(yuǎn)程推送階段預(yù)覽
以上配置完畢后穷当,使用PushMeBaby進(jìn)行推送預(yù)覽一下,以下是推送的內(nèi)容淹禾。
具體內(nèi)容的格式如下:具體格式摘自簡(jiǎn)書(shū)博主linatan博文-iOS10-UserNotifications
{
"aps":
{
"alert":
{
"title":"hello",
"subtitle" : "Session 01",
"body":"it is a beautiful day"
},
"category":"helloIdentifier",//比如我們想要使用自定義的UI,可以通過(guò)該值設(shè)置當(dāng)前request對(duì)象的identifier
"badge":1,
"mutable-content":1,//如果想要啟用Service的拓展,需要此數(shù)據(jù)茴扁,這個(gè)后面會(huì)有介紹
"sound":"default",
"image":"https://picjumbo.imgix.net/HNCK8461.jpg?q=40&w=200&sharp=30"
}
}
在進(jìn)行測(cè)試的過(guò)程中铃岔,樓主使用的遠(yuǎn)程推送如下:
//Demo中進(jìn)行推送的內(nèi)容如下:
{
"aps":
{
"alert" : "This is My message Yue.",
"badge" : "1",
"mutable-content" : 1
}
}
//pushMeBody中推送的字符串
self.payload = @"{\"aps\":{\"alert\":\"This is My message Yue.\",\"badge\":\"1\",\"mutable-content\":1}}";
如下:
本地進(jìn)行推送
項(xiàng)目中推送的聲明類(lèi)
對(duì)于Demo中大部分的測(cè)試,樓主都是使用了本地推送毁习,所以樓主將推送功能封裝成了RITLPushMessageManager
類(lèi)智嚷,聲明方法如下:
/// iOS10之前的本地推送
- (void)pushLocationNotificationbeforeiOS10;
//由于iOS10推送的可更新性,還新增了一個(gè)枚舉纺且,當(dāng)然盏道,本來(lái)是不需要的,但為了測(cè)試才有的這個(gè)枚舉類(lèi)型
typedef NS_ENUM(NSUInteger, RITLPushMessageType)
{
RITLPushMessageTypeNew = 0, /**<默認(rèn)為推送新的推送通知*/
RITLPushMessageTypeUpdate = 1, /**<更新當(dāng)前的推送通知*/
};
/// iOS10 之后的本地推送载碌,并根據(jù)類(lèi)型選擇是新的推送還是更新
- (void)pushLicationNotification:(nullable NSArray <UNNotificationAttachment *> *)attachments
pushType:(RITLPushMessageType)type NS_AVAILABLE_IOS(10_0);
UserNotifications以前本地推送
這個(gè)推送相信大家都是比較熟的了猜嘱,使用iOS10建議廢棄的UILocalNotification
類(lèi),實(shí)現(xiàn)方法如下:
-(void)pushLocationNotificationbeforeiOS10
{
UILocalNotification * localNotification = [[UILocalNotification alloc]init];
//設(shè)置消息體
localNotification.alertBody = @"RITL send a location notications (iOS9)";
#ifdef __IPHONE_8_2
//設(shè)置詳細(xì)內(nèi)容,iOS8.2才存在
localNotification.alertTitle = @"I am SubTitle";
#endif
//設(shè)置彈出時(shí)的圖
localNotification.alertLaunchImage = @"Stitch.png";
#ifdef __IPHONE_8_0
//拓展id
localNotification.category = RITLRequestIdentifier;
#endif
//觸發(fā)聲音
localNotification.soundName = UILocalNotificationDefaultSoundName;
//觸發(fā)標(biāo)志
localNotification.applicationIconBadgeNumber = 1;
//1秒之后觸發(fā)
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];
//注冊(cè)通知
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
UserNotifications下的本地推送
在新的框架下嫁艇,所有的推送都是使用了UNNotificationRequest
類(lèi)朗伶,這里有一點(diǎn)需要注意:媒體的url可以存儲(chǔ)在UNNotificationAttachment類(lèi)之中,但是必須是本地路徑步咪,如果是網(wǎng)絡(luò)圖论皆,需要先從網(wǎng)絡(luò)下載下來(lái),存到本地猾漫,然后將本地的路徑存儲(chǔ)進(jìn)行處理点晴;如果UNNotificationAttachment對(duì)象初始化失敗,會(huì)拋出異常悯周,導(dǎo)致程序崩潰粒督。比如Demo中就是通過(guò)選擇相冊(cè)的照片進(jìn)行本地化,再賦值路徑來(lái)進(jìn)行的本地推送队橙,實(shí)現(xiàn)方法如下:
-(void)pushLicationNotification:(NSArray <UNNotificationAttachment *> *)attachments pushType:(RITLPushMessageType)type
{
NSString * subTitle = (type == RITLPushMessageTypeNew ? @"I am a new SubTitle" : @"I am a update SubTitle");
//初始化信息對(duì)象
UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc]init];
//設(shè)置內(nèi)容
content.body = @"RITL send a location notications";
//設(shè)置詳細(xì)內(nèi)容
content.subtitle = subTitle;
//設(shè)置圖片名稱(chēng)
content.launchImageName = @"Stitch.png";
//設(shè)置拓展id
content.categoryIdentifier = RITLRequestIdentifier;
//設(shè)置推送聲音
content.sound = [UNNotificationSound defaultSound];
//設(shè)置通知
content.badge = @1;
//設(shè)置附帶信息
content.userInfo = @{@"RITL":@"I am RITL.",@"network":@"https://www.baidu.com"};
//媒體附帶信息
content.attachments = attachments;
#pragma mark - 延時(shí)發(fā)送
UNTimeIntervalNotificationTrigger * trigger = [UNNotificationTrigger defaultTimeIntervalNotificationTrigger];
//初始化通知請(qǐng)求
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:RITLRequestIdentifier content:content trigger:trigger];
//如果是更新坠陈,先移除
if (type == RITLPushMessageTypeUpdate)
[[UNUserNotificationCenter currentNotificationCenter]removeDeliveredNotificationsWithIdentifiers:@[RITLRequestIdentifier]];
//獲得推送控制中心
[[UNUserNotificationCenter currentNotificationCenter]addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if(error != nil)//出錯(cuò)
{
NSLog(@"error = %@",error.localizedDescription);
}
}];
}
UNNotificationTrigger 消息觸發(fā)類(lèi)
可能名字翻譯的不是那么正確,但個(gè)人的理解捐康,它就是負(fù)責(zé)觸發(fā)條件的仇矾,比如Demo中的類(lèi)別UNNotificationTrigger+RITLConveniceInitialize
中,為Demo提供了三種默認(rèn)的觸發(fā)條件解总,如下:
///默認(rèn)的延時(shí)推送觸發(fā)時(shí)機(jī)
+(UNTimeIntervalNotificationTrigger *)defaultTimeIntervalNotificationTrigger
{
return [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:1 repeats:false];
}
/// 默認(rèn)的日歷推送觸發(fā)時(shí)機(jī)
+(UNCalendarNotificationTrigger *)defaultCalendarNotificationTrigger
{
NSDateComponents * dateCompents = [NSDateComponents new];
dateCompents.hour = 7;//表示每天的7點(diǎn)進(jìn)行推送
return [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:dateCompents repeats:true];
}
/// 默認(rèn)的地域推送觸發(fā)時(shí)機(jī)
+(UNLocationNotificationTrigger *)defaultLocationNotificationTrigger
{
// 因?yàn)镃LRegion類(lèi)的初始化方法在iOS7提示廢棄贮匕,改用它的子類(lèi)CLCircularRegion
// CLRegion * region = [CLRegion alloc]initCircularRegionWithCenter:cooddinate2D(100,100) radius:200 identifier:locationTriggerIdentifier
//經(jīng)緯度分別都是100
CLLocationCoordinate2D coordinate2D = cooddinate2D(100,100);
//初始化范圍類(lèi)
CLCircularRegion * region = [[CLCircularRegion alloc]initWithCenter:coordinate2D radius:200 identifier:locationTriggerIdentifier];
return [UNLocationNotificationTrigger triggerWithRegion:region repeats:false];
}
本地推送階段預(yù)覽
樓主選擇使用在本地相冊(cè)中選擇一個(gè)圖片來(lái)完成本地推送,大體思路如下:
//該過(guò)程通過(guò)類(lèi)別UNNotificationAttachment+RITLConceniceInitialize實(shí)現(xiàn)
+(NSArray<UNNotificationAttachment *> *)defaultNotificationAttachmentsWithImage:(UIImage *)image
{
if (image == nil) return nil;
NSMutableArray <UNNotificationAttachment *> * attachments = [NSMutableArray arrayWithCapacity:1];
//將image存到本地
[RITLPushFilesManager saveImage:image key:imageTransformPathKey];
NSError * error;
UNNotificationAttachment * attachment = [UNNotificationAttachment attachmentWithIdentifier:attachmentIdentifier URL:[RITLPushFilesManager imageUrlPathWithKey:imageTransformPathKey] options:nil error:&error];
NSAssert(error == nil, error.localizedDescription);
[attachments addObject:attachment];
return [attachments mutableCopy];
}
預(yù)覽圖如下:
推送消息的策略
推送策略是什么呢花枫,可以理解為推送的一個(gè)"類(lèi)別"刻盐,當(dāng)我們使用3D Touch得以響應(yīng)推送信息之后,會(huì)出現(xiàn)類(lèi)似的一排按鈕劳翰,不過(guò)目前最大的限制為4個(gè)敦锌,并且最早出于iOS 8.0(那個(gè)時(shí)候沒(méi)有3D Touch感應(yīng),怎么能喚出拓展呢? 如果能告知的小伙伴佳簸,也請(qǐng)告知一下樓主乙墙,十分感謝)
同樣為了方便管理添加拓展,將添加拓展的方法封裝成RITLPushCategoryManager
類(lèi)
UserNotifications之前推送策略
// 為Demo添加默認(rèn)的策略--before iOS10
+(UIUserNotificationCategory *)addDefaultCategorysBeforeiOS10
{
//設(shè)置普通響應(yīng) -- 表示沒(méi)有便利初始化方法很坑
UIMutableUserNotificationAction * foregroundAction = [UIMutableUserNotificationAction new];
//設(shè)置屬性
foregroundAction.identifier = foregroundActionIdentifier;
foregroundAction.title = @"收到了";
foregroundAction.activationMode = UIUserNotificationActivationModeForeground;
//設(shè)置文本響應(yīng)
UIMutableUserNotificationAction * destructiveTextAction = [UIMutableUserNotificationAction new];
//設(shè)置屬性
destructiveTextAction.identifier = destructiveTextActionIdentifier;
destructiveTextAction.title = @"我想說(shuō)兩句";
destructiveTextAction.activationMode = UIUserNotificationActivationModeForeground;
destructiveTextAction.behavior = UIUserNotificationActionBehaviorTextInput;
destructiveTextAction.authenticationRequired = false;
destructiveTextAction.destructive = true;
//初始化Category
UIMutableUserNotificationCategory * category = [UIMutableUserNotificationCategory new];
//設(shè)置屬性
category.identifier = RITLRequestIdentifier;
[category setActions:@[foregroundAction,destructiveTextAction] forContext:UIUserNotificationActionContextDefault];
//返回
return [category copy];
}
//并在外部使用注冊(cè)推送的時(shí)候添加即可,代碼有點(diǎn)長(zhǎng),Demo中有演示
/*
[[UIApplication sharedApplication] registerUserNotificationSettings:categories:中的第二個(gè)參數(shù)听想,以NSSet的形式傳入即可
*/
UserNotifications下的推送策略
UserNotifications框架為我們提供了很多的便利初始化方法腥刹,從代碼也可以看出它的簡(jiǎn)潔性,由于是單行汉买,所以會(huì)顯得比較長(zhǎng) (相信我衔峰,在這里換行格式也是很難看的,所以索性用了一行 0.0)
+(void)addDefaultCategorys
{
// 設(shè)置響應(yīng)
UNNotificationAction * foregroundAction = [UNNotificationAction actionWithIdentifier:foregroundActionIdentifier title:@"收到了" options:UNNotificationActionOptionForeground];
// 設(shè)置文本響應(yīng)
UNTextInputNotificationAction * destructiveTextAction = [UNTextInputNotificationAction actionWithIdentifier:destructiveTextActionIdentifier title:@"我想說(shuō)兩句" options:UNNotificationActionOptionDestructive|UNNotificationActionOptionForeground textInputButtonTitle:@"發(fā)送" textInputPlaceholder:@"想說(shuō)什么?"];
// 初始化策略對(duì)象,這里的categoryWithIdentifier一定要與需要使用Category的UNNotificationRequest的identifier匹配(相同)才可觸發(fā)
UNNotificationCategory * category = [UNNotificationCategory categoryWithIdentifier:RITLRequestIdentifier actions:@[foregroundAction,destructiveTextAction] intentIdentifiers:@[foregroundActionIdentifier,destructiveTextActionIdentifier] options:UNNotificationCategoryOptionCustomDismissAction];
//直接通過(guò)UNUserNotificationCenter設(shè)置策略即可
[[UNUserNotificationCenter currentNotificationCenter]setNotificationCategories:[NSSet setWithObjects:category, nil]];
}
響應(yīng)策略的方式
如何響應(yīng)我們添加的策略呢蛙粘,這里就只介紹UserNotifications下的響應(yīng)方法垫卤,找到負(fù)責(zé)響應(yīng)最新推送協(xié)議方法的類(lèi)別RITLOriginPushAppDelegate+RITLUserNotifications:
在- (void)userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:
方法體最前面進(jìn)行一下策略判定即可,如下:
if ([response.actionIdentifier isEqualToString:foregroundActionIdentifier])
{
//可以彈出Alert提示一下
[self performSelector:NSSelectorFromString(@"__showAlert:") withObject:@"我是第一個(gè)策略動(dòng)作,收到了" afterDelay:0];
completionHandler();return;
}
else if([response.actionIdentifier isEqualToString:destructiveTextActionIdentifier])
{
[self performSelector:NSSelectorFromString(@"__showAlert:") withObject:[NSString stringWithFormat:@"我是第二個(gè)文本動(dòng)作组题,我輸入的文字是:%@",((UNTextInputNotificationResponse *)response).userText] afterDelay:0];
completionHandler();return;
}
推送策略階段預(yù)覽
推送拓展插件-NotificationContentExtension
創(chuàng)建方式
與Widget插件化開(kāi)發(fā)的創(chuàng)建步驟是一樣的呢,F(xiàn)ile->New->Target->NotificationContentExtension
</img>
創(chuàng)建成功之后崔列,默認(rèn)的控制器名稱(chēng)為NotificationViewController梢褐,Demo中僅僅改了一下名字,文件夾分布如下:
</img>
如果牽扯到UI的繪制的方法赵讯,數(shù)據(jù)與代碼的共享的方法盈咳,作為插件化開(kāi)發(fā)其實(shí)是一致的,可回顧一下博主之前的博文iOS開(kāi)發(fā)------Widget(Today Extension)插件化開(kāi)發(fā)感謝边翼。
這里需要注意一下鱼响,如果我們創(chuàng)建了拓展,但是本地推送的UI如果還是默認(rèn)的UI形式组底,這個(gè)時(shí)候就需要看一下Info.plist文件下 NSExtension->NSExtensionAttributes->UNNotificationExtensionCategory (默認(rèn)是一個(gè)String類(lèi)型)丈积,里面需要與UNNotificationRequest對(duì)象的identifier相匹配才可觸發(fā);如果支持多個(gè)通知债鸡,可以將UNNotificationExtensionCategory更改為數(shù)組類(lèi)型江滨。下面是一個(gè)栗子0.0
下面是Demo中進(jìn)行本地推送中初始化UNNotificationRequest對(duì)象的具體代碼形式:
//作為一個(gè)全局的固定字符串
NSString * const RITLRequestIdentifier = @"com.yue.originPush.myNotificationCategory";
//初始化通知請(qǐng)求
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:RITLRequestIdentifier content:content trigger:trigger];
同時(shí)為了保證能夠觸發(fā)自定義的推送UI,Demo中的plist文件設(shè)置如下:
</img>
在Demo中厌均,樓主依舊使用了storyboard進(jìn)行UI布局唬滑,也只因?yàn)槭茄芯靠蚣埽圆季质欠浅5暮?jiǎn)單棺弊,如下:
</img>
UNNotificationContentExtension協(xié)議
這個(gè)協(xié)議也是比較簡(jiǎn)單的晶密,只有兩個(gè)協(xié)議方法,有一個(gè)必須實(shí)現(xiàn)的方法模她,也有一個(gè)可選的稻艰。
// 收到推送消息后進(jìn)行的回調(diào),必須實(shí)現(xiàn)的方法
- (void)didReceiveNotification:(UNNotification *)notification
{
//獲得內(nèi)容對(duì)象
UNNotificationContent * content = notification.request.content;
//獲得需要展示的文本
NSString * customTitle = content.body;
//需要展示的圖片
UIImage * image = [UIImage imageNamed:content.launchImageName];
//設(shè)置
self.customlabel.text = customTitle;
//這是拓展里自定義的圖片
self.customimageView.image = image;
//這是通知里帶的照片
self.attachmentImageView.image = nil;//直接使用attachment對(duì)象的路徑進(jìn)行加載即可
//如果是遠(yuǎn)程推送的網(wǎng)絡(luò)圖沒(méi)有加載怎么辦侈净,這里不用擔(dān)心
//下面的NotificationServiceExtension就是解決這個(gè)問(wèn)題的
}
下面這個(gè)協(xié)議方法是可選的连锯。
它的作用就是归苍,當(dāng)我們使用自定義的UI進(jìn)行通知顯示的時(shí)候,通過(guò)點(diǎn)擊或者響應(yīng)策略Action的時(shí)候运怖,會(huì)優(yōu)先執(zhí)行該協(xié)議方法,并通過(guò)它的回調(diào)來(lái)決定下一步的操作夏伊。
//回調(diào)類(lèi)型的個(gè)人理解如下:
typedef NS_ENUM(NSUInteger, UNNotificationContentExtensionResponseOption)
{
UNNotificationContentExtensionResponseOptionDoNotDismiss, //默認(rèn)表示不消失
UNNotificationContentExtensionResponseOptionDismiss,//消失
UNNotificationContentExtensionResponseOptionDismissAndForwardAction,//消失并讓App對(duì)它進(jìn)行處理摇展,這個(gè)時(shí)候才會(huì)走原應(yīng)用的回調(diào)
};
// 展開(kāi)后的推送消息得到點(diǎn)擊響應(yīng)
- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption option))completion
{
//進(jìn)行回調(diào),這里是使其消失并回到主App進(jìn)行處理溺忧,處理完畢之后會(huì)走UNUserNotificationCenter的協(xié)議方法
completion(UNNotificationContentExtensionResponseOptionDismissAndForwardAction);
}
NotificationContentExtension階段預(yù)覽
推送拓展插件-NotificationServiceExtension
如果眼睛比較厲害的小伙伴估計(jì)上面的截圖也看到了,在NotificationContentExtension旁邊鲁森,如下圖:
</img>
這個(gè)插件的作用是什么呢祟滴,當(dāng)然這個(gè)插件主要是用于遠(yuǎn)程推送的數(shù)據(jù)處理,它能夠給我們最長(zhǎng)30秒的時(shí)間讓我們對(duì)遠(yuǎn)程推送的內(nèi)容進(jìn)行修改歌溉,或者對(duì)推送內(nèi)容中的圖片垄懂、視頻、音頻鏈接進(jìn)行下載的過(guò)程痛垛。
當(dāng)然大部分的代碼系統(tǒng)已經(jīng)幫我們寫(xiě)好了草慧,這里之貼上Demo中的一個(gè)小實(shí)現(xiàn),作用就是在收到遠(yuǎn)程推送的時(shí)候匙头,修改推送的內(nèi)容漫谷,如下:
//大約會(huì)給30秒的時(shí)間限制
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
//獲取request以及block對(duì)象
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
//對(duì)內(nèi)容可以進(jìn)行修改
self.bestAttemptContent.body = @"我是在Service里面修改后的title";
//如果如果含有圖片,視頻音頻的url蹂析,可以利用這里進(jìn)行下載coding...
//然后初始化UNNotificationAttachment對(duì)象舔示,賦值數(shù)組即可
//回調(diào)處理完畢的content內(nèi)容
self.contentHandler(self.bestAttemptContent);
}
下面的協(xié)議方法雖然能大體翻譯出來(lái),但卻不知道到底有什么用电抚,如果有知道的小伙伴惕稻,也請(qǐng)告知一下,十分感謝
//提供最后一個(gè)機(jī)會(huì)喻频,當(dāng)該拓展將被系統(tǒng)殺死的時(shí)候執(zhí)行的方法
- (void)serviceExtensionTimeWillExpire {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
//就比如上面英文介紹所說(shuō)的:可以用這個(gè)機(jī)會(huì)來(lái)表達(dá)一下你最想在content中表達(dá)的缩宜,不然的話最初推送的payload將被用到
self.contentHandler(self.bestAttemptContent);
}
NotificationServiceExtension階段預(yù)覽
出了文中提到的博文甥温,最后還要感謝下面博文對(duì)該博文的幫助:
iOS 10 新特性之通知推送--干貨一篇
活久見(jiàn)的重構(gòu) - iOS 10 UserNotifications 框架解析
最后祝大家工作開(kāi)心锻煌,明天就要為祖國(guó)慶生了,也祝大家節(jié)日快樂(lè)姻蚓。