上周,完成app新版本的开发。自从进入这版app的开发就没来更新过文章了,接下来会陆陆续续写一下在这过程中遇到的问题及一些总结。今天,就先说说消息推送吧。
推送,大家都不陌生,没有推送功能的app估计是少数。当然,对于推送介绍的文章也非常多也很仔细,但是真正开发过程中,还是会遇到各种问题,接下来会和大家谈谈集成推送的方法及我在开发中遇到的问题,但愿能给你们开发中遇到类似的问题时提供一种思路。
在推送过程我遇到的问题主要有:
1.在点击消息栏进入指定页面后,页面的点击事件不响应
2.点击消息栏进入指定页面后,页面的导航栏问题
其实这两个问题,归根结底是因为跳转时选择的控制器不对。比如当我点击消息栏时,希望跳转到A页面,A中没有导航栏。我开始用的是[self.window.rootViewController presentViewController:A animated:YES completion:nil]
,这时候跳转到A页面后,A页面中控件的点击事件不起作用了,后面发现,是因为层级图不对。而且用present
的话,没有导航栏,所以当你跳转到指定页面A时,如果A页面的点击事件还有跳转到新页面B的功能,那么这个当你通过A push
出来的B页面是没有导航栏的,而实际上我们希望的B是有导航栏的。
最后换成了pushViewController
的方法。这个方法的关键是,你得找到当前的的VC,比如A页面是通过tabbar的第一个item中的VC的nav
push出来的,那么这时候,只要找到tabbar第一个Item对应的nav中VC,然后用这个VC的 nav
push出A页面,问题就可以解决了。
接下来,说说该如何集成极光推送、如何处理收到通知时的页面跳转问题,以及如何设置标签和别名。
一、处理收到通知时页面跳转问题。
1.你需要到极光推送的平台上申请appkey,申请的过程中需要上传开发环境和正式环境的消息推送证书,所以如果你的app还没有消息推送证书,那么你需要到苹果的开发者账号中申请消息推送证书。如果对这个步骤不是很了解的话,可以参照iOS 证书 设置指南
2.申请好极光推送的appkey后,将下载的SDK拖到自己的工程中,添加相应的framework
,配置好相关信息。也可参照官方文档
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`中注册极光推送
-
(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为开发环境,YES为生产环境
//Required(2.1.5版本的SDK新增的注册方法,改成可上报IDFA,如果没有使用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;
}
注册deviceToken
pragma mark--注册devicetoken
-
(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[JPUSHService registerDeviceToken:deviceToken];
}
收到通知时的处理 ,这里需要注意一下,iOS10收到通知时的方法和iOS10以前的不一样
-
(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[JPUSHService handleRemoteNotification:userInfo];completionHandler(UIBackgroundFetchResultNewData);
if (_application.applicationState == UIApplicationStateActive) {
//程序运行时收到通知,先弹出消息框[self getPushMessageAtStateActive:userInfo];
}
else{
//程序已经关闭或者在后台运行
[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; // 收到推送的请求
UNNotificationContent *content = request.content; // 收到推送的消息内容
if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
[JPUSHService handleRemoteNotification:userInfo];
if (_application.applicationState == UIApplicationStateActive) {
//程序运行时收到通知,先弹出消息框
[self getPushMessageAtStateActive:userInfo];
}
else{
[self pushToViewControllerWhenClickPushMessageWith:userInfo];
}
}
// 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Alert三种类型可以设置
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) {
//程序运行时收到通知,先弹出消息框
[self getPushMessageAtStateActive:userInfo];
[[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationState" object:@"0"];
}
else{
[self pushToViewControllerWhenClickPushMessageWith:userInfo];
}
}
completionHandler(); // 系统要求执行这个方法
}
endif
如果在程序运行时收到通知,这时消息栏不会显示通知,所以如果想让用户收到通知的话,应该是给用户一个弹框提醒,告诉用户有消息通知,当用户点击提示框中的确认查看按钮时,跳转到指定的页面
pragma mark -- 程序运行时收到通知
-(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];
}
跳转到指定页面的方法。跳转到指定页面的话,可能会需要某些参数,这时可以根后台商定,根据参数跳转到相应的页面,同时也让后台把你需要的参数返回来。比如我和后台商定根据“pageType”这个参数确定跳转的页面。如果是跳转到次级页面,这里重要的是要找到正确的viewcontroller,用controller的nav进行push新页面。比如我的DetailViewController是用tabar的第一个item中的FirstViewController的nav进行push出来的,那么,当我点击通知消息想到跳转到DetailViewController,只要找到FirstViewController就可以了。
-(void)pushToViewControllerWhenClickPushMessageWith:(NSDictionary*)msgDic{
NSUserDefaults*pushJudge = [NSUserDefaults standardUserDefaults];
if ([[msgDic objectForKey:@"pageType"] integerValue]==0){
// 跳转到第一个tabbarItem,这时直接设置 UITabBarController的selectedIndex属性就可以
self.tabController.selectedIndex = 0;
}else if ([[msgDic objectForKey:@"pageType"] integerValue]==1){
//跳转到第二个tabbarItem
self.tabController.selectedIndex = 1;
}else if ([[msgDic objectForKey:@"pageType"] integerValue]==2){
//跳转到第三个tabbarItem
self.tabController.selectedIndex = 2;
}else if ([[msgDic objectForKey:@"pageType"] integerValue]==3){
//详情,这是从跳转到第一个tabbarItem跳转过去的,所以我们可以先让tabController.selectedIndex =0;然后找到VC的nav。
self.tabController.selectedIndex =0;
DetailViewController * VC = [[DetailViewController alloc]init];
[VC setHidesBottomBarWhenPushed:YES];
//因为我用了三方全屏侧滑手势,所以要找的是第一个tabbarController中的viewController的JTNavigationController ,接着再找JTNavigationController 里面的jt_viewControllers.lastObject,这样就可以找到FirstViewController了,然后跳转的时候就和正常的跳转一样了
JTNavigationController *nav=(JTNavigationController *)self.tabController.viewControllers[0];
UIViewController *vc=(UIViewController*)nav.jt_viewControllers.lastObject;
[vc.navigationController pushViewController:VC animated:NO];
}else {
}
}
二、如何设置标签或者别名
消息推送,有时候只想推送给指定的人或者指定的版本,那么这时候我们就需要对设备设置标签或者别名了,这样推送的时候可以根据标签或者别名推送给指定的用户
-
(void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];//标签
__autoreleasing NSMutableSet *tags = [NSMutableSet set];
//设置了IOS_ALL的标签,当推送时选了IOS_ALL这标签,那么只要装了这个app并且允许消息推送的用户就都能收到通知
[self setTags:&tags addTag:@"IOS_ALL"];NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
//根据版本号设置了IOSXX的标签,当推送时选了IOS2.1这标签,那么只要装了这个app并且允许消息推送的2.1版本的用户就都能收到通知[self setTags:&tags addTag:[NSString stringWithFormat:@"IOS%@",version]];
if (_isLogin==YES) {
//根据版本号设置了LoginUesr的标签,当推送时选了LoginUesr这标签,那么只要装了这个app并且允许消息推送的所有登录用户就都能收到通知
[self setTags:&tags addTag:@"LoginUesr"];
}else{}
//别名,根据用户的UID去设置别名,那么可以指定的推送给某些用户
__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回调:%@", 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--设置推送的标签及别名
-
(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;
}
}
}
当用户退出登录的时候可以重置别名,不然设备还是会收到通知
[JPUSHService setAlias:@"" callbackSelector:@selector(tagsAliasCallback:tags:alias:) object:self];
以上就是对于极光推送的简单介绍,如有问题欢迎指出或留言。
网友评论
[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];
UNUserNotificationCenter *center=[UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert|UNAuthorizationOptionBadge|UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted==YES) {
//开启
}else{
//关闭
}
}];
OC中是这样的,swift中应该也有与此对应的方法
@Available(iOS, introduced: 8.0, deprecated: 10.0, message: "Use UserNotifications Framework's UNAuthorizationOptions")
public struct UIUserNotificationType : OptionSet {
public init(rawValue: UInt)
public static var badge: UIUserNotificationType { get } // the application may badge its icon upon a notification being received
public static var sound: UIUserNotificationType { get } // the application may play a sound upon a notification being received
public static var alert: UIUserNotificationType { get } // the application may display an alert upon a notification being received
}
/*
typedef enum UNAuthorizationOptions : NSUInteger {
UNAuthorizationOptionBadge = (1 << 0),
UNAuthorizationOptionSound = (1 << 1),
UNAuthorizationOptionAlert = (1 << 2),
UNAuthorizationOptionCarPlay = (1 << 3)
} UNAuthorizationOptions;
*/
遇到两个问题,第一swift中UIUserNotificationType没有none枚举
第二:哪个方法判断iOS10?
UIViewController*controller = nav.visibleViewController;就可以获得当前显示的controller,如果推送的界面就是这个Controller那么显示,,,不是的话,,,,push
#pragma mark -- 程序运行时收到通知
-(void)getPushMessageAtStateActive:(NSDictionary *)pushMessageDic{} 我用你的方法,在前台时,不走那个方法, 但是当我点击要显示的界面,断点,那个方法,就开始走了
-(void)pushToViewControllerWhenClickPushMessageWith:(NSDictionary*)msgDic{