前言
首先我們要明白通知和推送是不一樣的莫秆。
通知是iOS操作系統(tǒng)層面上的功能幸缕,說白了就是iPhone上的通知條,通知中心等凤覆,App來了一條通知链瓦,系統(tǒng)來了升級(jí)通知,待辦事項(xiàng)來了一條通知盯桦,這里的通知指的是iOS操作系統(tǒng)內(nèi)的一個(gè)功能慈俯,更多體現(xiàn)在UI、交互拥峦、觸發(fā)邏輯贴膘、通知方式上。
推送指的是由APNs服務(wù)器略号、ProviderService刑峡、iOS系統(tǒng)、App構(gòu)成的通訊系統(tǒng)玄柠,是移動(dòng)互聯(lián)網(wǎng)與傳統(tǒng)的Web最明顯的區(qū)別的地方突梦。正因?yàn)橛辛送扑停瑢?shí)現(xiàn)了服務(wù)端能夠反向與用戶建立聯(lián)系随闪,而不是等待用戶訪問Web服務(wù)器阳似。
本文主要講的是通知
說明
從 iOS 10 新增的 UserNotifications Framework 可以發(fā)現(xiàn),Apple 整合了原有散亂的 API铐伴,并且增加了許多強(qiáng)大的功能撮奏。以 Apple 官方的角度來看俏讹,也必然是相當(dāng)重視推送服務(wù)對(duì) App 的影響、以及對(duì) Apple iOS 生態(tài)圈長(zhǎng)遠(yuǎn)發(fā)展的影響畜吊。
iOS10的通知新功能泽疆,用戶體驗(yàn)的提升和開發(fā)者能夠發(fā)揮的地方非常多,使得iOS更具有競(jìng)爭(zhēng)力玲献。
- iOS 10通知系統(tǒng)支持Images, GIFs, Audio and Video類型
- iOS 10推出Notification Service Extension與Notification Content Extension殉疼,可以實(shí)現(xiàn)推送數(shù)據(jù)在展示前進(jìn)行下載更新、定制通知UI
- iOS 10統(tǒng)一了通知類型捌年,具有時(shí)間間隔通知瓢娜、地理位置通知和日歷通知
User Notifications Framework 介紹:
關(guān)系圖:
重點(diǎn)介紹:
UNUserNotificationCenter通知中心,用以管理通知的注冊(cè)礼预、權(quán)限獲取和管理瞳腌、通知的刪除與更新袍镀,通過代理分發(fā)事件等。
UNNotification 通知實(shí)體,在UNUserNotificationCenter的代理回調(diào)事件中懒鉴,告知App接收到一條通知调鬓,包含一個(gè)發(fā)起通知的請(qǐng)求UNNotificationRequest
UNNotificationRequest包含通知內(nèi)容UNNotificationContent和觸發(fā)器UNNotificationTrigger
UNNotificationContent 通知內(nèi)容翩隧,通知的title窝革,sound,badge以及相關(guān)的圖像应结、聲音刨疼、視頻附件UNNotificationAttachment,觸發(fā)打開App時(shí)候指定的LacnchImage等
UNNotificationResponse摊趾,用戶在觸發(fā)了按鈕或者文本提交的UNNotificationAction的時(shí)候币狠,會(huì)形成一個(gè)response,通過通知中心的代理方法回調(diào)給App進(jìn)行處理或者是交給擴(kuò)展處理砾层。
UNNotificationServiceExtension漩绵,是一個(gè)在接收到APNs服務(wù)器推送過來的數(shù)據(jù)進(jìn)行處理的服務(wù)擴(kuò)展,如果App提供了服務(wù)擴(kuò)展肛炮,那么APNs下發(fā)推送后在通知顯示觸發(fā)之前止吐,會(huì)在UNNotificationServiceExtension內(nèi)接收到,此處有大約30秒的處理時(shí)間侨糟,開發(fā)者可以進(jìn)行一些數(shù)據(jù)下載碍扔、數(shù)據(jù)解密、更新等操作秕重,然后交由而后的內(nèi)容擴(kuò)展(UNNotificationContentExtension)或者是App進(jìn)行觸發(fā)顯示
UNNotificationCategory,用以定義一組樣式類型不同,該分類包含了某一個(gè)通知包含的交互動(dòng)作的組合,比如說UNNotificationRequest內(nèi)包含了一個(gè)Category標(biāo)示,那該通知就會(huì)以預(yù)定義好的交互按鈕或者文本框添加到通知實(shí)體上二拐。
UNNotificationAttachment服鹅,通知內(nèi)容UNNotificationContent包含的附件,一般為圖片百新、視頻和音頻企软,雖然iOS10的通知數(shù)據(jù)容量為4k,但依舊很少饭望,在添加了UNNotificationServiceExtension擴(kuò)展的情況下仗哨,可以在服務(wù)里下載圖片,生成圖片铅辞、視頻等的本地緩存厌漂,UNNotificationAttachment根據(jù)緩存數(shù)據(jù)生成并添加到UNNotificationContent中,交由UI顯示
UNNotificationAction巷挥,是通知中添加的action桩卵,展示在通知欄的下方。默認(rèn)以的button樣式展示倍宾。有一個(gè)文本輸入的子類UNTextInputNotificationAction∈どぃ可以在點(diǎn)擊button之后彈出一個(gè)鍵盤高职,輸入信息。用戶點(diǎn)擊信息和輸入的信息可以在UNNotificationResponse中獲取
系統(tǒng)級(jí)別的代理方法
// The method will be called on the delegate only if the application is in the foreground.
If the method is not implemented or the handler is not called in a timely manner
then the notification will not be presented.
#翻譯
[UNUserNotificationCenterDelegate willPresentNotification:withCompletionHandler:]
[UNUserNotificationCenterDelegate didReceiveNotificationResponse:withCompletionHandler:]
說了這么多辞州,現(xiàn)在源碼展示不同系統(tǒng)下的本地通知的實(shí)現(xiàn)怔锌,親測(cè)有效。
源碼實(shí)現(xiàn)iOS8-iOS10的本地通知
這里必須說一下iOS8-iOS10的本地通知跟 iOS10以后的不一樣变过,在應(yīng)用在前臺(tái)時(shí)是不會(huì)有橫幅或者彈框提示的埃元,只會(huì)觸發(fā)代理方法,想要展示本地通知媚狰,需要把應(yīng)用切換到后臺(tái)岛杀,當(dāng)時(shí)就很糾結(jié)為什么不出現(xiàn)提示,浪費(fèi)了很多時(shí)間崭孤。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerForRemoteNotifications)]) {
// 這里 types 可以自定義类嗤,如果 types 為 0,那么所有的用戶通知均會(huì)靜默的接收辨宠,系統(tǒng)不會(huì)給用戶任何提示(當(dāng)然遗锣,App 可以自己處理并給出提示)
UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert);
// 這里 categories 可暫不深入,本文后面會(huì)詳細(xì)講解嗤形。
UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
// 當(dāng)應(yīng)用安裝后第一次調(diào)用該方法時(shí)精偿,系統(tǒng)會(huì)彈窗提示用戶是否允許接收通知
[[UIApplication sharedApplication] registerUserNotificationSettings:mySettings];
}
[self registerLocalNotificationInOldWay:10];
return YES;
}
-(void)registerLocalNotificationInOldWay:(NSInteger)alertTime {
UILocalNotification *notification = [[UILocalNotification alloc] init];
// 設(shè)置觸發(fā)通知的時(shí)間
NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:alertTime];
NSLog(@"fireDate=%@",fireDate);
notification.fireDate = fireDate;
// 時(shí)區(qū)
notification.timeZone = [NSTimeZone defaultTimeZone];
// 設(shè)置重復(fù)的間隔
notification.repeatInterval = kCFCalendarUnitSecond;
// 通知內(nèi)容
notification.alertBody = @"該起床了...";
notification.applicationIconBadgeNumber = 1;
// 通知被觸發(fā)時(shí)播放的聲音
notification.soundName = UILocalNotificationDefaultSoundName;
// 通知參數(shù)
NSDictionary *userDict = [NSDictionary dictionaryWithObject:@"開始學(xué)習(xí)iOS開發(fā)了" forKey:@"key"];
notification.userInfo = userDict;
// ios8后,需要添加這個(gè)注冊(cè),才能得到授權(quán)
if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {
UIUserNotificationType type = UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound;
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:type
categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
// 通知重復(fù)提示的單位笔咽,可以是天墓阀、周、月
notification.repeatInterval = NSCalendarUnitDay;
} else {
// 通知重復(fù)提示的單位拓轻,可以是天斯撮、周、月
notification.repeatInterval = NSDayCalendarUnit;
}
// 執(zhí)行通知注冊(cè)
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
//在后臺(tái)情況下點(diǎn)擊本地推送 或者 在前臺(tái)收到本地通知都會(huì)觸發(fā)這個(gè)方法
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSLog(@"didReceiveLocalNotification notification");
application.applicationIconBadgeNumber -= 1;
}
源碼實(shí)現(xiàn)iOS10以后的本地通知
iOS10以后不論應(yīng)用在前臺(tái)或者在后臺(tái)扶叉,都可以展示本地通知勿锅。
- (void)viewDidLoad {
[super viewDidLoad];
if ([[UIDevice currentDevice] systemVersion].floatValue >= 10.0) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
//點(diǎn)擊允許
NSLog(@"注冊(cè)通知成功");
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
[self registerNotification:5];
}];
} else {
//點(diǎn)擊不允許
NSLog(@"注冊(cè)通知失敗");
}
}];
//注冊(cè)推送(同iOS8)
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
}
/** * 描述 使用 UNNotification 本地通知(iOS 10) * @param alerTime 多長(zhǎng)時(shí)間后進(jìn)行推送 **/
-(void)registerNotification:(NSInteger)alerTime
{
// 1、創(chuàng)建通知內(nèi)容枣氧,注:這里得用可變類型的UNMutableNotificationContent溢十,否則內(nèi)容的屬性是只讀的
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
// 標(biāo)題
content.title = @"這是通知";
// 次標(biāo)題
content.subtitle = @"這是通知subtitle";
// 內(nèi)容
content.body = @"這是通知body這是通知body這是通知body這是通知body這是通知body這是通知body";
// app顯示通知數(shù)量的角標(biāo)
content.badge = [NSNumber numberWithInteger:4];
// 通知的提示聲音,這里用的默認(rèn)的聲音
content.sound = [UNNotificationSound defaultSound];
NSURL *imageUrl = [[NSBundle mainBundle] URLForResource:@"cell2@2x" withExtension:@"png"];
UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:@"imageIndetifier" URL:imageUrl options:nil error:nil];
// 附件 可以是音頻达吞、圖片张弛、視頻 這里是一張圖片
content.attachments = @[attachment];
// 標(biāo)識(shí)符
content.categoryIdentifier = @"categoryIndentifier";
// 2、創(chuàng)建通知觸發(fā)
/* 觸發(fā)器分三種: UNTimeIntervalNotificationTrigger : 在一定時(shí)間后觸發(fā)酪劫,如果設(shè)置重復(fù)的話吞鸭,timeInterval不能小于60 UNCalendarNotificationTrigger : 在某天某時(shí)觸發(fā),可重復(fù) UNLocationNotificationTrigger : 進(jìn)入或離開某個(gè)地理區(qū)域時(shí)觸發(fā) */
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:10 repeats:NO];
// NSDateComponents *components = [NSDateComponents new];
// components.second = 2.0f;
// UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:NO];
// 3覆糟、創(chuàng)建通知請(qǐng)求
UNNotificationRequest *notificationRequest = [UNNotificationRequest requestWithIdentifier:@"KFGroupNotification" content:content trigger:trigger];
// 4刻剥、將請(qǐng)求加入通知中心
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:notificationRequest withCompletionHandler:^(NSError * _Nullable error) {
if (error == nil) {
NSLog(@"已成功加推送%@",notificationRequest.identifier);
}
}];
}
#pragma mark - iOS10 推送代理
//不實(shí)現(xiàn),通知不會(huì)有提示
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
//應(yīng)用在前臺(tái)收到通知
NSLog(@"========%@", notification);
UNNotificationRequest *request = notification.request; // 原始請(qǐng)求
NSDictionary * userInfo = notification.request.content.userInfo;//userInfo數(shù)據(jù)
UNNotificationContent *content = request.content; // 原始內(nèi)容
NSString *title = content.title; // 標(biāo)題
NSString *subtitle = content.subtitle; // 副標(biāo)題
NSNumber *badge = content.badge; // 角標(biāo)
NSString *body = content.body; // 推送消息體
UNNotificationSound *sound = content.sound; // 指定的聲音
//建議將根據(jù)Notification進(jìn)行處理的邏輯統(tǒng)一封裝滩字,后期可在Extension中復(fù)用~
//如果需要在應(yīng)用在前臺(tái)也展示通知
completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionSound|UNNotificationPresentationOptionAlert); // 回調(diào)block造虏,將設(shè)置傳入
}
// 對(duì)通知進(jìn)行響應(yīng)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler {
// 根據(jù)類別標(biāo)識(shí)符處理目標(biāo)反應(yīng)
if ([response.notification.request.content.categoryIdentifier isEqualToString:@"categoryIndentifier"]) {
[self handleResponse:response];
UNNotificationRequest *request = response.notification.request; // 原始請(qǐng)求
UNNotificationContent *content = request.content; // 原始內(nèi)容
NSString *title = content.title; // 標(biāo)題
NSString *subtitle = content.subtitle; // 副標(biāo)題
NSNumber *badge = content.badge; // 角標(biāo)
NSString *body = content.body; // 推送消息體
UNNotificationSound *sound = content.sound;
//在此,可判斷response的種類和request的觸發(fā)器是什么麦箍,可根據(jù)遠(yuǎn)程通知和本地通知分別處理漓藕,再根據(jù)action進(jìn)行后續(xù)回調(diào)
}
completionHandler();
}
- (void)handleResponse:(UNNotificationResponse *)response {
NSString *actionIndentifier = response.actionIdentifier;
[UIApplication sharedApplication].applicationIconBadgeNumber = 0;
NSLog(@"%@",@"處理通知");
}
觸發(fā)器 UNNotificationTrigger有四個(gè)子類:
- UNPushNotificationTrigger,遠(yuǎn)程推送觸發(fā)器挟裂,一般是遠(yuǎn)程推送推過來的通知帶有這類觸發(fā)器
- UNTimeIntervalNotificationTrigger享钞,時(shí)間間隔觸發(fā)器,定時(shí)或者是重復(fù)话瞧,在本地推送設(shè)置中有用
- UNCalendarNotificationTrigger嫩与,日歷觸發(fā)器,指定日期進(jìn)行通知
- UNLocationNotificationTrigger交排,地理位置觸發(fā)器划滋,指定觸發(fā)通知的條件是地理位置CLRegion這個(gè)類型。
觸發(fā)器和內(nèi)容最后形成UNNotificationRequest埃篓,一個(gè)通知請(qǐng)求处坪,本地通知的請(qǐng)求,直接交給通知中心進(jìn)行發(fā)送,發(fā)送成功后同窘,該通知會(huì)按照觸發(fā)器的觸發(fā)條件進(jìn)行觸發(fā)玄帕,并且會(huì)顯示到通知中心上,用戶可與指定的category交互方式與通知進(jìn)行交互
小結(jié)
本地通知其實(shí)很有用處想邦,可以做為一種提示使用裤纹,做類似布卡漫畫這種也可以推送正在追的漫畫的新章節(jié)并在后臺(tái)靜默下載,待到下載好再給用戶發(fā)送一個(gè)本地推送丧没,用戶點(diǎn)開即看無需再聯(lián)網(wǎng)鹰椒。