[TOC]
一杆融、工作機(jī)制:
APNS 是 Apple Push Notification Service 的縮寫(xiě)褥紫,是蘋(píng)果服務(wù)器亿扁。
工作流程如下所示:
- 首先app向iOS注冊(cè)推送消息
- iOS收到app的注冊(cè)后如蚜,向APNS Server索要deviceToken择镇,app接收返回的deviceToken
- app把接收到的deviceToken傳給我們的服務(wù)器
- 當(dāng)我們的服務(wù)器需要推送消息時(shí)挡逼,就把要推送的消息和deviceToken發(fā)送給APNS Server
- APNS Server服務(wù)將推送的消息發(fā)送給app
二、app代碼實(shí)現(xiàn):
1腻豌、注冊(cè)消息推送:
#import <UserNotifications/UserNotifications.h>
// 注冊(cè)APNS推送
- (void)registerRemoteNotification {
if (@available(iOS 10.0, *)) {
UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
// 必須寫(xiě)代理家坎,不然無(wú)法監(jiān)聽(tīng)通知的接收與點(diǎn)擊事件
notificationCenter.delegate = self;
// 請(qǐng)求授權(quán)
[notificationCenter requestAuthorizationWithOptions:UNAuthorizationOptionAlert
completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted && !error) {
// 用戶(hù)同意授權(quán),注冊(cè)遠(yuǎn)程推送
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
// 用戶(hù)不同意授權(quán)
}
}];
// 獲取用戶(hù)同意的或者更改的推送權(quán)限設(shè)置
[notificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
NSLog(@"%@",settings);
}];
} else {
// iOS8 ~ iOS10
if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
} else {
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert];
}
}
}
2吝梅、把拿到到deviceToken傳給我們的服務(wù)器:
// 獲取deviceToken成功
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// 把deviceToken轉(zhuǎn)為NSString
NSString *deviceStr = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
deviceStr = [deviceStr stringByReplacingOccurrencesOfString:@" " withString:@""];
// 把deviceToken傳給我們自己的服務(wù)器
}
// 獲取deviceToken失敗
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(@"error:%@",error.description);
}
3虱疏、app接收到遠(yuǎn)程推送的消息:
#pragma mark iOS10 收到通知(本地和遠(yuǎn)程)
// App接收通知時(shí)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler API_AVAILABLE(ios(10.0)){
// 收到推送的請(qǐng)求
UNNotificationRequest *request = notification.request;
// 收到推送的內(nèi)容
UNNotificationContent *content = request.content;
// 收到推送的基本信息
NSDictionary *userInfo = content.userInfo;
// 收到推送消息的角標(biāo)
NSNumber *badge = content.badge;
// 推送消息的聲音
UNNotificationSound *sound = content.sound;
// 推送消息的副標(biāo)題
NSString *subtitle = content.subtitle;
// 推送消息的標(biāo)題
NSString *title = content.title;
// 推送消息的類(lèi)型
if ([request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
// 遠(yuǎn)程通知
} else if ([request.trigger isKindOfClass:[UNTimeIntervalNotificationTrigger class]]) {
// 本地通知,一定時(shí)間之后苏携,重復(fù)或者不重復(fù)推送通知 我們可以設(shè)置timeInterval(時(shí)間間隔)和repeats(是否重復(fù))做瞪。
} else if ([request.trigger isKindOfClass:[UNCalendarNotificationTrigger class]]) {
// 本地通知,一定日期之后右冻,重復(fù)或者不重復(fù)推送通知 例如装蓬,你每天8點(diǎn)推送一個(gè)通知,只要dateComponents為8纱扭,如果你想每天8點(diǎn)都推送這個(gè)通知矛物,只要repeats為YES就可以了
} else if ([request.trigger isKindOfClass:[UNLocationNotificationTrigger class]]) {
// 本地通知,地理位置的一種通知跪但,當(dāng)用戶(hù)進(jìn)入或離開(kāi)一個(gè)地理區(qū)域來(lái)通知履羞。
}
// 執(zhí)行下面方法,選擇提醒用戶(hù)的方式
completionHandler(UNNotificationPresentationOptionBadge|
UNNotificationPresentationOptionSound|
UNNotificationPresentationOptionAlert);
}
// App通知的點(diǎn)擊事件屡久,只會(huì)是用戶(hù)點(diǎn)擊消息才會(huì)觸發(fā)忆首,如果使用戶(hù)長(zhǎng)按(3DTouch)、Action等并不會(huì)觸發(fā)被环。
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(ios(10.0)){
// 收到推送的請(qǐng)求
UNNotificationRequest *request = response.notification.request;
// 糙及。。筛欢。
// 系統(tǒng)要求執(zhí)行這個(gè)方法
completionHandler();
}
#pragma mark iOS10 之前收到通知
// iOS10以下收到本地推送通知
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
}
// iOS7及以上收到通知
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
completionHandler(UIBackgroundFetchResultNewData);
}
// iOS6及以下收到通知
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
}
三浸锨、本地推送:
本地推送這里主要分兩個(gè)版本來(lái)介紹,一個(gè)是iOS 10及以后版姑,一個(gè)是iOS 10以前柱搜;
1、iOS 10及以上本地推送生成流程:
創(chuàng)建一個(gè)觸發(fā)器 trigger
創(chuàng)建推送的內(nèi)容 UNMutableNotificationContent
創(chuàng)建推送請(qǐng)求 UNNotificationRequest
-
推送請(qǐng)求添加到推送管理中心 UNUserNotificationCenter
- (void)postLocalNotificatonUpper10 API_AVAILABLE(ios(10.0)) { // 1. 創(chuàng)建一個(gè)觸發(fā)器(trigger),這里以 UNTimeIntervalNotificationTrigger 為例 UNTimeIntervalNotificationTrigger *timeTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:30 repeats:NO]; // 2. 創(chuàng)建推送的內(nèi)容 UNMutableNotificationContent UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; content.title = @"title"; content.subtitle = @"subtitle"; content.body = @"body"; content.badge = @2; content.sound = [UNNotificationSound defaultSound]; content.userInfo = @{@"key1" : @"value1", @"key2" : @"value2"}; // // 推送交互操作 // content.categoryIdentifier = @"Dely_locationCategory"; // [self addNotificationAction]; // 3. 創(chuàng)建推送請(qǐng)求 UNNotificationRequest UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"identifier" content:content trigger:timeTrigger]; // 4. 推送請(qǐng)求添加到推送管理中心 UNUserNotificationCenter UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { if (!error) { NSLog(@"推送已添加成功"); } }]; }
2剥险、iOS 10以下本地推送生成流程:
- 創(chuàng)建本地通知對(duì)象 UILocalNotification
- 把本地通知對(duì)象加入到app日程表中
- (void)postLocalNotificatonBelow10 {
/*
fireDate:?jiǎn)?dòng)時(shí)間
timeZone:?jiǎn)?dòng)時(shí)間參考的時(shí)區(qū)
repeatInterval:重復(fù)推送時(shí)間(NSCalendarUnit類(lèi)型)聪蘸,0代表不重復(fù)
repeatCalendar:重復(fù)推送時(shí)間(NSCalendar類(lèi)型)
alertBody:通知內(nèi)容
alertAction:解鎖滑動(dòng)時(shí)的事件
alertLaunchImage:?jiǎn)?dòng)圖片,設(shè)置此字段點(diǎn)擊通知時(shí)會(huì)顯示該圖片
alertTitle:通知標(biāo)題,適用iOS8.2之后
applicationIconBadgeNumber:收到通知時(shí)App icon的角標(biāo)
soundName:推送是帶的聲音提醒健爬,設(shè)置默認(rèn)的字段為UILocalNotificationDefaultSoundName
userInfo:發(fā)送通知時(shí)附加的內(nèi)容
category:此屬性和注冊(cè)通知類(lèi)型時(shí)有關(guān)聯(lián)控乾,(有興趣的同學(xué)自己了解,不詳細(xì)敘述)適用iOS8.0之后
region:帶有定位的推送相關(guān)屬性娜遵,具體使用見(jiàn)下面【帶有定位的本地推送】適用iOS8.0之后
regionTriggersOnce:帶有定位的推送相關(guān)屬性蜕衡,具體使用見(jiàn)下面【帶有定位的本地推送】適用iOS8.0之后
*/
// 1. 創(chuàng)建本地通知對(duì)象 UILocalNotification
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:30];
localNotification.alertBody = @"通知顯示內(nèi)容";
localNotification.alertAction = @"解鎖滑動(dòng)是的事件";
localNotification.applicationIconBadgeNumber = 1;
localNotification.soundName = UILocalNotificationDefaultSoundName;
// 2. 把本地通知對(duì)象加入到app日程表中
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
// 立即發(fā)送通知
// [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
}
四、iOS 10推送交互操作:
iOS 10中设拟,可以允許推送添加交互操作 action衷咽,這些 action 使得app可以在前臺(tái)或者后臺(tái)執(zhí)行一些邏輯代碼。這是推送功能的一個(gè)拓展蒜绽,可通過(guò)3D-Touch觸發(fā)镶骗,或者右滑會(huì)出現(xiàn)view和clear選項(xiàng)來(lái)觸發(fā)。
- 創(chuàng)建action
- 創(chuàng)建category
- 把category添加到通知中心
// 添加推送交互操作
- (void)addNotificationAction API_AVAILABLE(ios(10.0)) {
/*
1.需要解鎖顯示躲雅,點(diǎn)擊不會(huì)進(jìn)app
UNNotificationActionOptionAuthenticationRequired
2.點(diǎn)擊不會(huì)進(jìn)app
UNNotificationActionOptionDestructive
3.點(diǎn)擊會(huì)進(jìn)app
UNNotificationActionOptionForeground
*/
// 1. 創(chuàng)建action
UNNotificationAction *lookAction =
[UNNotificationAction actionWithIdentifier:@"action.join"
title:@"接收邀請(qǐng)"
options:UNNotificationActionOptionAuthenticationRequired];
UNNotificationAction *joinAction =
[UNNotificationAction actionWithIdentifier:@"action.look"
title:@"查看邀請(qǐng)"
options:UNNotificationActionOptionForeground];
UNNotificationAction *cancelAction =
[UNNotificationAction actionWithIdentifier:@"action.cancel"
title:@"取消"
options:UNNotificationActionOptionDestructive];
// 2. 創(chuàng)建category
// * identifier 標(biāo)識(shí)符
// * actions 操作數(shù)組
// * intentIdentifiers 意圖標(biāo)識(shí)符 可在 <Intents/INIntentIdentifiers.h> 中查看鼎姊,主要是針對(duì)電話、carplay 等開(kāi)放的 API相赁。
// * options 通知選項(xiàng) 枚舉類(lèi)型 也是為了支持 carplay
UNNotificationCategory *category =
[UNNotificationCategory categoryWithIdentifier:@"Dely_locationCategory"
actions:@[lookAction, joinAction, cancelAction]
intentIdentifiers:@[]
options:UNNotificationCategoryOptionCustomDismissAction];
// 3. 把category添加到通知中心
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObject:category]];
}
// 添加推送文本輸入交互操作
- (void)addTextNotificationAction API_AVAILABLE(ios(10.0)) {
// 創(chuàng)建 UNTextInputNotificationAction 比 UNNotificationAction 多了兩個(gè)參數(shù)
// * buttonTitle 輸入框右邊的按鈕標(biāo)題
// * placeholder 輸入框占位符
UNTextInputNotificationAction *inputAction =
[UNTextInputNotificationAction actionWithIdentifier:@"action.input"
title:@"輸入"
options:UNNotificationActionOptionForeground
textInputButtonTitle:@"發(fā)送"
textInputPlaceholder:@"tell me loudly"];
// 注冊(cè) category
UNNotificationCategory *notificationCategory =
[UNNotificationCategory categoryWithIdentifier:@"Dely_locationCategory"
actions:@[inputAction]
intentIdentifiers:@[]
options:UNNotificationCategoryOptionCustomDismissAction];
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObject:notificationCategory]];
}
- 事件的操作
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(ios(10.0)){
// iOS 10推送交互事件的操作相寇,在這里處理
//點(diǎn)擊或輸入action
NSString* actionIdentifierStr = response.actionIdentifier;
//輸入
if ([response isKindOfClass:[UNTextInputNotificationResponse class]]) {
NSString* userSayStr = [(UNTextInputNotificationResponse *)response userText];
}
//點(diǎn)擊
if ([actionIdentifierStr isEqualToString:@"action.join"]) {
} else if ([actionIdentifierStr isEqualToString:@"action.look"]) {
}
// 系統(tǒng)要求執(zhí)行這個(gè)方法
completionHandler();
}
注意,遠(yuǎn)程推送一定要保證 category 的鍵值對(duì)是一致的
{
"aps" : {
"alert" : {
"title" : "iOS遠(yuǎn)程消息钮科,我是主標(biāo)題唤衫!-title",
"subtitle" : "iOS遠(yuǎn)程消息,我是主標(biāo)題绵脯!-Subtitle",
"body" : "Dely,why am i so handsome -body"
},
"category" : "Dely_locationCategory",
"badge" : "2"
}
}