上周,完成app新版本的開發(fā)。自從進(jìn)入這版app的開發(fā)就沒(méi)來(lái)更新過(guò)文章了,接下來(lái)會(huì)陸陸續(xù)續(xù)寫一下在這過(guò)程中遇到的問(wèn)題及一些總結(jié)。今天,就先說(shuō)說(shuō)消息推送吧。
推送,大家都不陌生,沒(méi)有推送功能的app估計(jì)是少數(shù)。當(dāng)然,對(duì)于推送介紹的文章也非常多也很仔細(xì),但是真正開發(fā)過(guò)程中,還是會(huì)遇到各種問(wèn)題,接下來(lái)會(huì)和大家談?wù)劶赏扑偷姆椒拔以陂_發(fā)中遇到的問(wèn)題,但愿能給你們開發(fā)中遇到類似的問(wèn)題時(shí)提供一種思路。
在推送過(guò)程我遇到的問(wèn)題主要有:
1.在點(diǎn)擊消息欄進(jìn)入指定頁(yè)面后,頁(yè)面的點(diǎn)擊事件不響應(yīng)
2.點(diǎn)擊消息欄進(jìn)入指定頁(yè)面后,頁(yè)面的導(dǎo)航欄問(wèn)題
其實(shí)這兩個(gè)問(wèn)題,歸根結(jié)底是因?yàn)樘D(zhuǎn)時(shí)選擇的控制器不對(duì)。比如當(dāng)我點(diǎn)擊消息欄時(shí),希望跳轉(zhuǎn)到A頁(yè)面,A中沒(méi)有導(dǎo)航欄。我開始用的是[self.window.rootViewController presentViewController:A animated:YES completion:nil],這時(shí)候跳轉(zhuǎn)到A頁(yè)面后,A頁(yè)面中控件的點(diǎn)擊事件不起作用了,后面發(fā)現(xiàn),是因?yàn)閷蛹?jí)圖不對(duì)。而且用present的話,沒(méi)有導(dǎo)航欄,所以當(dāng)你跳轉(zhuǎn)到指定頁(yè)面A時(shí),如果A頁(yè)面的點(diǎn)擊事件還有跳轉(zhuǎn)到新頁(yè)面B的功能,那么這個(gè)當(dāng)你通過(guò)A push出來(lái)的B頁(yè)面是沒(méi)有導(dǎo)航欄的,而實(shí)際上我們希望的B是有導(dǎo)航欄的。
最后換成了pushViewController的方法。這個(gè)方法的關(guān)鍵是,你得找到當(dāng)前的的VC,比如A頁(yè)面是通過(guò)tabbar的第一個(gè)item中的VC的navpush出來(lái)的,那么這時(shí)候,只要找到tabbar第一個(gè)Item對(duì)應(yīng)的nav中VC,然后用這個(gè)VC的 nav
push出A頁(yè)面,問(wèn)題就可以解決了。
接下來(lái),說(shuō)說(shuō)該如何集成極光推送、如何處理收到通知時(shí)的頁(yè)面跳轉(zhuǎn)問(wèn)題,以及如何設(shè)置標(biāo)簽和別名。
一、處理收到通知時(shí)頁(yè)面跳轉(zhuǎn)問(wèn)題。
1.你需要到極光推送的平臺(tái)上申請(qǐng)appkey,申請(qǐng)的過(guò)程中需要上傳開發(fā)環(huán)境和正式環(huán)境的消息推送證書,所以如果你的app還沒(méi)有消息推送證書,那么你需要到蘋果的開發(fā)者賬號(hào)中申請(qǐng)消息推送證書。如果對(duì)這個(gè)步驟不是很了解的話,可以參照iOS 證書 設(shè)置指南
2.申請(qǐng)好極光推送的appkey后,將下載的SDK拖到自己的工程中,添加相應(yīng)的framework,配置好相關(guān)信息。也可參照官方文檔
iOS SDK 集成指南
3.在 AppDelegate.m引入
#import "JPUSHService.h"
#ifdef NSFoundationVersionNumber_iOS_9_x_Max
#import <UserNotifications/UserNotifications.h>
#endif```
在` - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions`中注冊(cè)極光推送
-
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
[self initRootViewController];
if ([[UIDevice currentDevice].systemVersion floatValue] >= 10.0) {
//iOS10以上
JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];
entity.types = UNAuthorizationOptionAlert|UNAuthorizationOptionBadge|UNAuthorizationOptionSound;
[JPUSHService registerForRemoteNotificationConfig:entity delegate:self];
}else if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
//iOS8以上可以添加自定義categories
[JPUSHService registerForRemoteNotificationTypes:(UIUserNotificationTypeBadge |
UIUserNotificationTypeSound |
UIUserNotificationTypeAlert)
categories:nil];
}
else {
//iOS8以下categories 必須為nil
[JPUSHService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeSound |
UIRemoteNotificationTypeAlert)
categories:nil];
}
BOOL isProduction = NO;// NO為開發(fā)環(huán)境,YES為生產(chǎn)環(huán)境
//Required(2.1.5版本的SDK新增的注冊(cè)方法,改成可上報(bào)IDFA,如果沒(méi)有使用IDFA直接傳nil
[JPUSHService setupWithOption:launchOptions appKey:@"你的極光推送appkey"
channel:nil
apsForProduction:isProduction
advertisingIdentifier:nil];
//2.1.9版本新增獲取registration id block接口。可以獲取registrationID
[JPUSHService registrationIDCompletionHandler:^(int resCode, NSString *registrationID) {
if(resCode == 0){
NSLog(@"registrationID獲取成功:%@",registrationID);
}
else{
NSLog(@"registrationID獲取失敗,code:%d",resCode);
}
}];
return YES;
}
注冊(cè)deviceToken
pragma mark--注冊(cè)devicetoken
-
(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[JPUSHService registerDeviceToken:deviceToken];
}
收到通知時(shí)的處理 ,這里需要注意一下,iOS10收到通知時(shí)的方法和iOS10以前的不一樣
-
(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[JPUSHService handleRemoteNotification:userInfo];completionHandler(UIBackgroundFetchResultNewData);
if (_application.applicationState == UIApplicationStateActive) {
//程序運(yùn)行時(shí)收到通知,先彈出消息框[self getPushMessageAtStateActive:userInfo];}
else{
//程序已經(jīng)關(guān)閉或者在后臺(tái)運(yùn)行
[self pushToViewControllerWhenClickPushMessageWith:userInfo];}
[application setApplicationIconBadgeNumber:0];
[JPUSHService handleRemoteNotification:userInfo];
completionHandler(UIBackgroundFetchResultNewData);
}
ifdef NSFoundationVersionNumber_iOS_9_x_Max
pragma mark- JPUSHRegisterDelegate
-(void)jpushNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler {
NSDictionary * userInfo = notification.request.content.userInfo;
UNNotificationRequest *request = notification.request; // 收到推送的請(qǐng)求
UNNotificationContent *content = request.content; // 收到推送的消息內(nèi)容
if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
[JPUSHService handleRemoteNotification:userInfo];
if (_application.applicationState == UIApplicationStateActive) {
//程序運(yùn)行時(shí)收到通知,先彈出消息框
[self getPushMessageAtStateActive:userInfo];
}
else{
[self pushToViewControllerWhenClickPushMessageWith:userInfo];
}
}
// 需要執(zhí)行這個(gè)方法,選擇是否提醒用戶,有Badge、Sound、Alert三種類型可以設(shè)置
completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionSound|UNNotificationPresentationOptionAlert);
}
//
-(void)jpushNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
NSDictionary * userInfo = response.notification.request.content.userInfo;
[[NSUserDefaults standardUserDefaults] setObject:@"Inactive" forKey:@"applicationState"];
if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
[JPUSHService handleRemoteNotification:userInfo];
if (_application.applicationState == UIApplicationStateActive) {
//程序運(yùn)行時(shí)收到通知,先彈出消息框
[self getPushMessageAtStateActive:userInfo];
[[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationState" object:@"0"];
}
else{
[self pushToViewControllerWhenClickPushMessageWith:userInfo];
}
}
completionHandler(); // 系統(tǒng)要求執(zhí)行這個(gè)方法
}
endif
如果在程序運(yùn)行時(shí)收到通知,這時(shí)消息欄不會(huì)顯示通知,所以如果想讓用戶收到通知的話,應(yīng)該是給用戶一個(gè)彈框提醒,告訴用戶有消息通知,當(dāng)用戶點(diǎn)擊提示框中的確認(rèn)查看按鈕時(shí),跳轉(zhuǎn)到指定的頁(yè)面
pragma mark -- 程序運(yùn)行時(shí)收到通知
-(void)getPushMessageAtStateActive:(NSDictionary *)pushMessageDic{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@""
message:[[pushMessageDic objectForKey:@"aps"]objectForKey:@"alert"]
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"查看"
style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[self pushToViewControllerWhenClickPushMessageWith:pushMessageDic];
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消"
style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}];
[alertController addAction:confirmAction];
[alertController addAction:cancelAction];
[self.window.rootViewController presentViewController:alertController animated:YES completion:nil];
}
跳轉(zhuǎn)到指定頁(yè)面的方法。跳轉(zhuǎn)到指定頁(yè)面的話,可能會(huì)需要某些參數(shù),這時(shí)可以根后臺(tái)商定,根據(jù)參數(shù)跳轉(zhuǎn)到相應(yīng)的頁(yè)面,同時(shí)也讓后臺(tái)把你需要的參數(shù)返回來(lái)。比如我和后臺(tái)商定根據(jù)“pageType”這個(gè)參數(shù)確定跳轉(zhuǎn)的頁(yè)面。如果是跳轉(zhuǎn)到次級(jí)頁(yè)面,這里重要的是要找到正確的viewcontroller,用controller的nav進(jìn)行push新頁(yè)面。比如我的DetailViewController是用tabar的第一個(gè)item中的FirstViewController的nav進(jìn)行push出來(lái)的,那么,當(dāng)我點(diǎn)擊通知消息想到跳轉(zhuǎn)到DetailViewController,只要找到FirstViewController就可以了。
-(void)pushToViewControllerWhenClickPushMessageWith:(NSDictionary*)msgDic{
NSUserDefaults*pushJudge = [NSUserDefaults standardUserDefaults];
if ([[msgDic objectForKey:@"pageType"] integerValue]==0){
// 跳轉(zhuǎn)到第一個(gè)tabbarItem,這時(shí)直接設(shè)置 UITabBarController的selectedIndex屬性就可以
self.tabController.selectedIndex = 0;
}else if ([[msgDic objectForKey:@"pageType"] integerValue]==1){
//跳轉(zhuǎn)到第二個(gè)tabbarItem
self.tabController.selectedIndex = 1;
}else if ([[msgDic objectForKey:@"pageType"] integerValue]==2){
//跳轉(zhuǎn)到第三個(gè)tabbarItem
self.tabController.selectedIndex = 2;
}else if ([[msgDic objectForKey:@"pageType"] integerValue]==3){
//詳情,這是從跳轉(zhuǎn)到第一個(gè)tabbarItem跳轉(zhuǎn)過(guò)去的,所以我們可以先讓tabController.selectedIndex =0;然后找到VC的nav。
self.tabController.selectedIndex =0;
DetailViewController * VC = [[DetailViewController alloc]init];
[VC setHidesBottomBarWhenPushed:YES];
//因?yàn)槲矣昧巳饺羵?cè)滑手勢(shì),所以要找的是第一個(gè)tabbarController中的viewController的JTNavigationController ,接著再找JTNavigationController 里面的jt_viewControllers.lastObject,這樣就可以找到FirstViewController了,然后跳轉(zhuǎn)的時(shí)候就和正常的跳轉(zhuǎn)一樣了
JTNavigationController *nav=(JTNavigationController *)self.tabController.viewControllers[0];
UIViewController *vc=(UIViewController*)nav.jt_viewControllers.lastObject;
[vc.navigationController pushViewController:VC animated:NO];
}else {
}
}
二、如何設(shè)置標(biāo)簽或者別名
消息推送,有時(shí)候只想推送給指定的人或者指定的版本,那么這時(shí)候我們就需要對(duì)設(shè)備設(shè)置標(biāo)簽或者別名了,這樣推送的時(shí)候可以根據(jù)標(biāo)簽或者別名推送給指定的用戶
-
(void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];//標(biāo)簽
__autoreleasing NSMutableSet *tags = [NSMutableSet set];
//設(shè)置了IOS_ALL的標(biāo)簽,當(dāng)推送時(shí)選了IOS_ALL這標(biāo)簽,那么只要裝了這個(gè)app并且允許消息推送的用戶就都能收到通知
[self setTags:&tags addTag:@"IOS_ALL"];NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
//根據(jù)版本號(hào)設(shè)置了IOSXX的標(biāo)簽,當(dāng)推送時(shí)選了IOS2.1這標(biāo)簽,那么只要裝了這個(gè)app并且允許消息推送的2.1版本的用戶就都能收到通知[self setTags:&tags addTag:[NSString stringWithFormat:@"IOS%@",version]];
if (_isLogin==YES) {
//根據(jù)版本號(hào)設(shè)置了LoginUesr的標(biāo)簽,當(dāng)推送時(shí)選了LoginUesr這標(biāo)簽,那么只要裝了這個(gè)app并且允許消息推送的所有登錄用戶就都能收到通知
[self setTags:&tags addTag:@"LoginUesr"];
}else{}
//別名,根據(jù)用戶的UID去設(shè)置別名,那么可以指定的推送給某些用戶
__autoreleasing NSString *alias ;if (_userUID!= nil) {
alias =[NSString stringWithFormat:@"%@",_userUID];
}
[self analyseInput:&alias tags:&tags];
[JPUSHService setTags:tags alias:alias callbackSelector:@selector(tagsAliasCallback:tags:alias:) object:self];
}
-
(void)tagsAliasCallback:(int)iResCode
tags:(NSSet *)tags
alias:(NSString *)alias {
NSString *callbackString =
[NSString stringWithFormat:@"%d, \ntags: %@, \nalias: %@\n", iResCode,
[self logSet:tags], alias];NSLog(@"TagsAlias回調(diào):%@", callbackString);
} (NSString *)logSet:(NSSet *)dic {
if (![dic count]) {
return nil;
}
NSString *tempStr1 =
[[dic description] stringByReplacingOccurrencesOfString:@"\u"
withString:@"\U"];
NSString *tempStr2 =
[tempStr1 stringByReplacingOccurrencesOfString:@""" withString:@"\""];
NSString *tempStr3 =
[[@""" stringByAppendingString:tempStr2] stringByAppendingString:@"""];
NSData *tempData = [tempStr3 dataUsingEncoding:NSUTF8StringEncoding];
NSString *str =
[NSPropertyListSerialization propertyListFromData:tempData
mutabilityOption:NSPropertyListImmutable
format:NULL
errorDescription:NULL];
return str;
}
pragma mark--設(shè)置推送的標(biāo)簽及別名
-
(void)setTags:(NSMutableSet **)tags addTag:(NSString *)tag {
[*tags addObject:tag];
} (void)analyseInput:(NSString **)alias tags:(NSSet *)tags {
// alias analyse
if (![alias length]) {
// ignore alias
alias = nil;
}
// tags analyse
if (![tags count]) {
tags = nil;
} else {
__block int emptyStringCount = 0;
[tags enumerateObjectsUsingBlock:^(NSString *tag, BOOL *stop) {
if ([tag isEqualToString:@""]) {
emptyStringCount++;
} else {
emptyStringCount = 0;
stop = YES;
}
}];
if (emptyStringCount == [tags count]) {
*tags = nil;
}
}
}
當(dāng)用戶退出登錄的時(shí)候可以重置別名,不然設(shè)備還是會(huì)收到通知
[JPUSHService setAlias:@"" callbackSelector:@selector(tagsAliasCallback:tags:alias:) object:self];
以上就是對(duì)于極光推送的簡(jiǎn)單介紹,如有問(wèn)題歡迎指出或留言。