離上次寫iOS集成極光推送之后盈电,我還得這樣做總結(jié)已過(guò)去半年了,半年的時(shí)間,在碼代碼的過(guò)程中,慢慢的體會(huì)到了一點(diǎn),看到需求精拟、設(shè)計(jì)不要急于寫代碼,先在腦子里想想,該怎么設(shè)計(jì),應(yīng)該考慮哪些方面的問(wèn)題,怎么編寫效率更高燎斩,用戶體驗(yàn)更好等等虱歪?這樣才能更快的提升。
有關(guān)iOS10推送的介紹和使用栅表,早有很多前輩已寫好了博客笋鄙,這里我就不介紹怎么使用推送了,著重介紹本地推送在實(shí)際項(xiàng)目中的使用流程怪瓶,以及需要注意的地方萧落。蘋果提供本地推送這么一個(gè)功能,我想一般是用來(lái)結(jié)合網(wǎng)絡(luò)洗贰、時(shí)間針對(duì)性的提醒某些用戶的找岖。
相比遠(yuǎn)程推送,1.本地推送沒(méi)有相關(guān)證書方面的要求哆姻;2.在沒(méi)有自己平臺(tái)推送服務(wù)器的情況下宣增,可以在app內(nèi)部對(duì)用戶進(jìn)行推送提醒。而遠(yuǎn)程推送最大的優(yōu)點(diǎn)就是可以輕松利用三方平臺(tái)矛缨,無(wú)論用戶是否有網(wǎng)絡(luò),都能隨時(shí)對(duì)app用戶進(jìn)行推送帖旨,當(dāng)然也可以對(duì)個(gè)別用戶進(jìn)行推送箕昭。關(guān)鍵還得看需求~
談?wù)勛罱窘拥囊粋€(gè)項(xiàng)目中的一個(gè)需求,以下是項(xiàng)目需求:
用戶在使用APP的過(guò)程中解阅,要能及時(shí)(30s)看到他的交易信息落竹,
1. 在APP內(nèi)以彈窗的形式提醒用戶,點(diǎn)擊“查看”跳轉(zhuǎn)到對(duì)應(yīng)消息界面货抄;
2. 在后臺(tái)運(yùn)行時(shí)述召,以推送通知的形式提醒用戶,用戶點(diǎn)擊消息跳轉(zhuǎn)到對(duì)應(yīng)的消息界面蟹地。
由于沒(méi)有自己的推送服務(wù)器积暖,又涉及到網(wǎng)絡(luò)請(qǐng)求,暫不考慮APP處于退出狀態(tài)時(shí)的情況怪与。
看到這么一個(gè)需求夺刑,當(dāng)時(shí)就想到用推送,至于遠(yuǎn)程推還是本地推分别,當(dāng)然選擇后者遍愿,遠(yuǎn)程推的話必須借助極光推送平臺(tái),而且不能滿足從公司后臺(tái)獲取用戶實(shí)時(shí)交易的消息耘斩,并有針對(duì)性的給用戶推送沼填。
下面說(shuō)說(shuō)實(shí)現(xiàn)思路,涉及到iOS10推送API
括授、讀取用戶對(duì)通知權(quán)限的設(shè)置
坞笙、定時(shí)器
轧邪、通知中心
、界面跳轉(zhuǎn)
羞海、定時(shí)器和通知的移除
忌愚,以下關(guān)于本地通知都用iOS10的API。
1.給APP申請(qǐng)通知權(quán)限却邓,
2.讀取用戶對(duì)通知的設(shè)置硕糊;
3.創(chuàng)建定時(shí)器;
4.添加通知中心觀察者腊徙;
5.創(chuàng)建 UNUserNotificationCenter對(duì)象简十,配置推送內(nèi)容;
6.分別處理前臺(tái)和后臺(tái)接收通知的情況撬腾;
7.用戶點(diǎn)擊通知后的界面跳轉(zhuǎn)螟蝙;
8.移除定時(shí)器、通知中心對(duì)象民傻。
開(kāi)始上代碼:
- 1.給APP申請(qǐng)通知權(quán)限
// 1.給APP申請(qǐng)通知權(quán)限,關(guān)于請(qǐng)求權(quán)限(定位等)務(wù)必放在APPdelegate中,程序一啟動(dòng)馬上提醒用戶選擇.否則設(shè)置-通知中心根本就沒(méi)有此應(yīng)用程序的通知設(shè)置,自己想去后臺(tái)設(shè)置打開(kāi)都找不到地方胰默。
// 這是iOS10請(qǐng)求通知權(quán)限的API
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) {
// 用戶對(duì)通知的設(shè)置
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
DLog(@"%@", (long)settings.authorizationStatus == 2 ? @"成獲取到通知權(quán)限" : @"用戶關(guān)閉了通知");
if (settings.authorizationStatus != 2) {
DLog(@"用戶關(guān)閉了通知");
}
}];
}];
- 2.檢查用戶對(duì)通知功能的設(shè)置狀態(tài),我放在首頁(yè)viewDidLoad中漓踢,當(dāng)用戶一進(jìn)入首頁(yè)如果發(fā)現(xiàn)沒(méi)有打開(kāi)則以彈窗的形式提醒用戶牵署,引導(dǎo)其打開(kāi);否則不進(jìn)行接下來(lái)的操作喧半。注:以下代碼都放在首頁(yè)控制器中奴迅。
// 2.檢查用戶對(duì)通知功能的設(shè)置狀態(tài)
- (BOOL)isPermissionedPushNotification {
if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) {
DLog(@"用戶關(guān)閉了通知功能");
// 彈窗
UIAlertController *alertvc = [UIAlertController alertControllerWithTitle:@"溫馨提醒" message:@"請(qǐng)打開(kāi)通知功能,否則無(wú)法收到交易提醒." preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"去設(shè)置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
DLog(@"引導(dǎo)用戶去設(shè)置中心打開(kāi)通知");
// 點(diǎn)擊跳轉(zhuǎn)至設(shè)置中心
NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if([[UIApplication sharedApplication] canOpenURL:url]) {
NSURL *url =[NSURL URLWithString:UIApplicationOpenSettingsURLString];
[[UIApplication sharedApplication] openURL:url];
}
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"忽略交易提醒" style:UIAlertActionStyleCancel handler:nil];
[alertvc addAction:cancelAction];
[alertvc addAction:confirmAction];
[self presentViewController:alertvc animated:YES completion:nil];
return false;
} else {
DLog(@"可以進(jìn)行推送");
return true;
}
}
- 3.創(chuàng)建定時(shí)器,獲得新消息未讀數(shù)
- 4.創(chuàng)建通知中心觀察者
- (void)viewDidLoad {
[super viewDidLoad];
// ...
if ([self isPermissionedPushNotification])
{
// 3.創(chuàng)建定時(shí)器挺据,獲得新消息未讀數(shù)
self.timer = [NSTimer scheduledTimerWithTimeInterval:RequestTimeInterval target:self selector:@selector(setupUnreadTradeMsg) userInfo:nil repeats:YES];
// 設(shè)置timer模式
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
// 4.創(chuàng)建通知中心觀察者
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(confirmReadNoticeContent:) name:@"ReadNoticeContent" object:nil];
}
// 定時(shí)器執(zhí)行方法
- (void)setupUnreadTradeMsg {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[NetworkEngine signPostWithURL:PARAMQUERY parmas:params success:^(id requestResult) {
// ...
// 省略部分網(wǎng)絡(luò)請(qǐng)求代碼
// 設(shè)置app角標(biāo)數(shù)量 = 未讀消息數(shù)量
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge];
// 消息按鈕紅點(diǎn)
[self.msgBtn setBackgroundImage:[UIImage imageNamed:@"new_message"] forState:UIControlStateNormal];
// 將新的交易信息內(nèi)容傳入推送
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
// ...
// 發(fā)起推送
[self pushNotificationOfTradeMsg:params];
} failure:^(NSError *error) {
// [self showToast:@"請(qǐng)求失敗,請(qǐng)檢查網(wǎng)絡(luò)連接"];
return ;
}];
});
- 5.配置推送內(nèi)容
// 5.配置推送內(nèi)容
- (void)pushNotificationOfTradeMsg:(NSDictionary *)params {
// 1取具、創(chuàng)建通知對(duì)象
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
// 必須設(shè)置代理,否則無(wú)法收到通知
center.delegate = self;
// 權(quán)限
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
// 2扁耐、創(chuàng)建通知內(nèi)容
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
// 標(biāo)題
content.title = params[@"title"];
content.body = params[@"content"];
// 聲音
content.sound = [UNNotificationSound soundNamed:@"notification.wav"];
// 圖片
NSURL *imageUrl = [[NSBundle mainBundle] URLForResource:@"msgImg" withExtension:@"png"];
UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:@"imageIndetifier" URL:imageUrl options:nil error:nil];
content.attachments = @[attachment];
// 標(biāo)識(shí)符
content.categoryIdentifier = @"categoryIndentifier";
// 3暇检、創(chuàng)建通知觸發(fā)時(shí)間
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:1.0 repeats:NO];
// 4、創(chuàng)建通知請(qǐng)求
UNNotificationRequest *notificationRequest = [UNNotificationRequest requestWithIdentifier:@"KFGroupNotification" content:content trigger:trigger];
// 5做葵、將請(qǐng)求加入通知中心
[center addNotificationRequest:notificationRequest withCompletionHandler:^(NSError * _Nullable error) {
if (!error) {
DLog(@"已成功加入通知請(qǐng)求");
} else {
DLog(@"出錯(cuò)啦%@", [error localizedDescription]);
}
}];
}
}];
}
- 6.分別處理前臺(tái)和后臺(tái)接收通知的情況
/** 6.1當(dāng)用戶處于前臺(tái)時(shí),消息發(fā)送前走這個(gè)方法 */
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{
DLog(@"--------通知即將發(fā)出-------");
// 在前臺(tái)時(shí),通過(guò)此方法監(jiān)聽(tīng)到消息發(fā)出,不讓其在通知欄顯示,以彈窗的形式展示出來(lái);設(shè)置聲音提示
completionHandler(UNNotificationPresentationOptionSound);
// 獲取消息內(nèi)容
NSMutableDictionary *content = [[NSMutableDictionary alloc] init];
[content setObject:notification.request.content.title forKey:@"content"];
[content setObject:notification.request.content.body forKey:@"body"];
// 彈窗
UIAlertController *alertvc = [UIAlertController alertControllerWithTitle:notification.request.content.title message:notification.request.content.body preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* updateAction = [UIAlertAction actionWithTitle:@"查看" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
// 創(chuàng)建通知對(duì)象
NSNotification *notice = [NSNotification notificationWithName:@"ReadNoticeContent" object:nil userInfo:content];
// 發(fā)送通知
[[NSNotificationCenter defaultCenter] postNotification:notice];
DLog(@"點(diǎn)擊查看消息");
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"忽略" style:UIAlertActionStyleCancel handler:nil];
[alertvc addAction:cancelAction];
[alertvc addAction:updateAction];
[self presentViewController:alertvc animated:YES completion:nil];
}
/** 6.2不處于前臺(tái)時(shí),與通知交互走這個(gè)方法 */
-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
completionHandler(UIBackgroundFetchResultNewData);
// 獲取消息內(nèi)容
NSMutableDictionary *content = [[NSMutableDictionary alloc] init];
[content setObject:response.notification.request.content.title forKey:@"content"];
[content setObject:response.notification.request.content.body forKey:@"body"];
// 判斷是否為本地通知
if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
NSLog(@"收到遠(yuǎn)程通知");
} else {
// 判斷為本地通知
//創(chuàng)建通知對(duì)象
NSNotification *notice = [NSNotification notificationWithName:@"ReadNoticeContent" object:nil userInfo:content];
//發(fā)送通知
[[NSNotificationCenter defaultCenter] postNotification:notice];
}
}
- 7.用戶點(diǎn)擊通知后的界面跳轉(zhuǎn)
// 點(diǎn)擊查看消息占哟,進(jìn)行界面跳轉(zhuǎn)
- (void)confirmReadNoticeContent:(NSDictionary *)content {
DLog(@"跳轉(zhuǎn)頁(yè)面 %@", content);
// 點(diǎn)擊通知查看內(nèi)容,角標(biāo)清零,紅點(diǎn)消失
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[self.msgBtn setBackgroundImage:[UIImage imageNamed:@"nav_message"] forState:UIControlStateNormal];
// 獲取當(dāng)前跟控制器
UIViewController *rootVC = nil;
UIWindow *window = [UIApplication sharedApplication].windows.firstObject;
if ([window.rootViewController isKindOfClass:[GFNavigationController class]])
{
rootVC = [(GFNavigationController *)window.rootViewController visibleViewController];
}
else if ([window.rootViewController isKindOfClass:[GFTabBarController class]])
{
GFTabBarController *tabVC = (GFTabBarController *)window.rootViewController;
rootVC = [(GFNavigationController *)[tabVC selectedViewController] visibleViewController];
}
else
{
rootVC = window.rootViewController;
}
// 跳轉(zhuǎn)到消息列表頁(yè)面
TradeMessageViewController *tmvc = [[TradeMessageViewController alloc] init];
[rootVC.navigationController pushViewController:tmvc animated:YES];
}
- 8.移除定時(shí)器酿矢、通知中心對(duì)象
- (void)dealloc {
// 移除通知
[[NSNotificationCenter defaultCenter] removeObserver:self];
// 銷毀定時(shí)器
[self.timer invalidate];
self.timer = nil;
}
至此榨乎,消息已經(jīng)可以成功發(fā)出,從前臺(tái)點(diǎn)擊查看或從后臺(tái)點(diǎn)擊都可以成功進(jìn)行界面跳轉(zhuǎn)瘫筐,然后返回到之前的頁(yè)面蜜暑。
總結(jié):
1、UNUserNotificationCenter對(duì)象的創(chuàng)建及內(nèi)容配置可以根據(jù)需要放在控制器中策肝,不一定非得放在APPdelegate中肛捍;
2隐绵、上面前臺(tái)和后臺(tái)點(diǎn)擊查看消息內(nèi)容
是以發(fā)通知的形式進(jìn)行處理的,其實(shí)可以直接用[self methodName]的形式調(diào)用拙毫,原因是接收通知的方法和點(diǎn)擊查看消息的方法在同一個(gè)控制器中依许,如果不在同一個(gè)控制器中(跨界面)就得用通知進(jìn)行接收了。
3缀蹄、這里我的跟控制器是TabBar峭跳,所以界面跳轉(zhuǎn)走的是第二個(gè)else if,如果是導(dǎo)航控制器或其他缺前,會(huì)跟著變的蛀醉。
最后,由于是客戶的項(xiàng)目衅码,就沒(méi)有單獨(dú)整理demo拯刁。本地通知實(shí)際運(yùn)用并不難,自己稍微花點(diǎn)時(shí)間整理下逝段,就全部弄明白了垛玻,思路流程都已經(jīng)列出來(lái)了,可以根據(jù)需要一步一步來(lái)惹恃,這個(gè)弄清楚夭谤,其實(shí)遠(yuǎn)程通知也差不多了。好了巫糙,如果發(fā)現(xiàn)有不對(duì)或者需要優(yōu)化的地方,請(qǐng)指出颊乘,真心感謝参淹!