作者:Ezreallp
一次偶然的机会,公司的项目要用到推送,我自己本来就很懒,不愿意去弄整套APNS的流程,刚好之前跟朋友聊起过他们的产品中集成了个推的Android推送,说是体验还可以,那这次我就试一下他们的iOS推送。于是抱着试一试的心态,我先建个demo,试着去集成一下个推iOS推送SDK,摸索着完成了整个流程,言归正传,直接上硬菜!
##如何集成个推iOS SDK
看了个推的官网,发现他们集成的方式有两种,分别是XCode集成和CocoaPods集成。本人比较懒,越简单越好,越轻松越好,毫不犹豫的选择了Cocoapods集成方式,程序猿么,就是要想尽办法的懒,搞起!
###CocoaPods集成
1.安装CocoaPods
安装方式简单, Mac 下都自带 ruby,使用 ruby 的 gem 命令即可下载安装:
$ sudo gem install cocoapods $ pod setup
2.准备Podfile文件
在我们的工程目录下,新建一个名为Podfile的文件,如下格式,将依赖的库名字依次列在文件中即可:
作者这里使用的是标准版本:
target 'GeTuipush' do platform :ios, "7.0" pod 'GTSDK' end target 'NotificationService' do platform :ios, "10.0" pod 'GTExtensionSDK' end
3.完成GTSDK导入
在项目根目录中执行如下命令:
$ pod install
执行完成后,项目目录结构如下图所示:
注意:在pod install之前,首先你的工程必须创建好,并且如果Podfile文件里面如果有target:NotificationService,那在pod install之前需要创建好通知扩展的Target。
4.开启推送功能:既然是推送,当然是要开推送功能啦!:
5.后台运行权限设置:看个推的官网上面说是为了更好的支持消息推送,提供更多的推送样式,提高消息到达率,既然这么说了,那就不管三七二十一先开了再说,如下图所示:
6.XCode10建议开启WiFi信息授权:在 Xcode 10.x 以上,找到应用Target设置中的Capabilities -> Access WiFi Information,确认开关已经设为ON状态。如下图所示:
注意:主Target和通知扩展的Target都需要打开
7.代码部分,下来就是我们程序猿最喜欢的部分了,粘贴复制。由于是第一次集成个推SDK的代码,我还是仔细的研究了下。
####初始化SDK注册APNs并获取CID
1.为AppDelegate增加回调接口类:
#import <UIKit/UIKit.h> #import <GTSDK/GeTuiSdk.h> // iOS10 及以上需导入 UserNotifications.framework #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 #import <UserNotifications/UserNotifications.h> #endif @interface AppDelegate : UIResponder <UIApplicationDelegate, GeTuiSdkDelegate, UNUserNotificationCenterDelegate> @property (strong, nonatomic) UIWindow *window; @end
2.初始化SDK并注册APNs:
#import "AppDelegate.h" /// 个推开发者网站中申请App时,注册的AppId、AppKey、AppSecret #define kGtAppId @"GVZZTqh7lu6S4VLMacneZ7" #define kGtAppKey @"RRYDFjGzO17TJXZfGeTuq3" #define kGtAppSecret @"7BXDJ0IgWF6a8M0xCgo4G" @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. [GeTuiSdk startSdkWithAppId:kGtAppId appKey:kGtAppKey appSecret:kGtAppSecret delegate:self]; // 注册 APNs [self registerRemoteNotification]; return YES; }
注册APNs获取DeviceToken:
/** 注册 APNs */ - (void)registerRemoteNotification { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; center.delegate = self; [center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionCarPlay) completionHandler:^(BOOL granted, NSError *_Nullable error) { if (!error) { NSLog(@"request authorization succeeded!"); } }]; [[UIApplication sharedApplication] registerForRemoteNotifications]; }
个推demo里面给开发者提供演示代码,根据APP支持的iOS系统不同,进行修改。我们的工程最低支持iOS10。
获取CID信息:
/** SDK启动成功返回cid */ - (void)GeTuiSdkDidRegisterClient:(NSString *)clientId { NSLog(@"clientId:%@", clientId); }
这三个参数kGtAppId、kGtAppKey、kGtAppSecret是干啥用的,这三个参数如何获取?回头又看了下个推的官网才搞明白,正好记录下如何申请者三个参数,跟我应用的bundleID绑定。
####如何获取kGtAppId、kGtAppKey、kGtAppSecret
1.创建个推开发者账号
访问个推开发者中心,申请个推账号
2.登记新应用
注意:登记新应用是在应用管理页面而不是消息推送页面。
在登记应用界面填写应用名和应用表示,勾选个推产品,勾选iOS,填写包名和bundleID,如下图所示:
这里我有点疑惑。创建应用的时候想勾选iOS,但是看到默认选择了Android平台,并要填写Android签名,这签名是what,这如何搞?看到跟前有个提示如何获取,点了一下,发现里面有SHA256的签名,抱着试一试的态度,直接copy过来,呦呵,能用哦,心里美滋滋。
提交成功后就可以获取到kGtAppId、kGtAppKey、kGtAppSecret,将三个参数填入我们的工程中,然后运行工程,在GeTuiSdkDidRegisterClient的回调方法中获取到cid了,嗯,看来我们已经成功了一一小部分了,距离成功还要继续加油。
####注册DeviceToken并统计APNs通知的点击数
1.向个推服务器注册DeviceToken:
/** 远程通知注册成功委托 */ - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { //向个推服务器注册deviceToken 为了方便开发者,建议使用新方法 NSLog(@"deviceToken:%@",deviceToken); [GeTuiSdk registerDeviceTokenData:deviceToken]; }
2.处理APNs通知点击事件:
因为我们的工程最低适配到iOS10,这里我就只添加了iOS10及以后版本的通知点击事件,要是想兼容iOS10以下的,可以在个推的demo中找到。
iOS 10及以后版本,处理APNs通知点击事件
// iOS 10: 点击通知进入App时触发,在该方法内统计有效用户点击数 - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler { NSLog(@"didReceiveNotification:%@", response.notification.request.content.userInfo); // [ GTSdk ]:将收到的APNs信息传给个推统计 [GeTuiSdk handleRemoteNotification:response.notification.request.content.userInfo]; completionHandler(); }
3.接受个推通道下发的透传消息:
/** SDK收到透传消息回调 */ - (void)GeTuiSdkDidReceivePayloadData:(NSData *)payloadData andTaskId:(NSString *)taskId andMsgId:(NSString *)msgId andOffLine:(BOOL)offLine fromGtAppId:(NSString *)appId { //收到个推消息 NSString *payloadMsg = nil; if (payloadData) { payloadMsg = [[NSString alloc] initWithBytes:payloadData.bytes length:payloadData.length encoding:NSUTF8StringEncoding]; } NSString *msg = [NSString stringWithFormat:@"taskId=%@,messageId:%@,payloadMsg:%@%@",taskId,msgId, payloadMsg,offLine ? @"<离线消息>" : @""]; NSLog(@"\n>>>[GexinSdk ReceivePayload]:%@\n\n", msg); }
获取到了透传消息,但是当应用在后台或者应用杀死的情况下,我们如何获取到APNs消息,这里我们就需要在个推平台用到推送证书,如何获取推送证书?因为本人也是第一次搞推送这书,而且也踩了不少的坑,为了下次不再踩同样的坑,所以在这里就对如何制作推送证书进行了一次规整。
####如何制作推送证书?
1.进入苹果开发者中心,选择证书选项,如下图所示:
2.创建推送证书之前必须创建一个APPID,因为推送证书是和APPID绑定在一起的,如下图所示:
在下面的App Services中选择允许推送(Push Notifications),如下图所示:
3.APPID创建好了之后,这个时候需要去创建推送证书,而且还要根据需要的环境选择对应的推送证书,包括开发环境推送证书和生产环境推送证书,然后还要跟刚才创建好的APPID相关联,如下图所示:
这个时候需要上传CSR文件,我们回到桌面,打开钥匙串,从颁发机构申请证书并保存到本地磁盘,如下图所示:
这样CSR文件就创建好了,我们回到苹果开发者中心,继续创建我们的推送证书,选择保存到本地的CSR文件,如下图所示:
这样,我们的推送证书就创建完成了,在本地下载中找到下载的推送证书并双击添加到钥匙串中,然后打开钥匙串找到创建好的推送证书,右键导出P12证书,并输入证书密码,如下图所示:
4.打开我们的个推开发者中心,在个推·消息推送-应用列表-应用配置”中上传正确的APNs证书,如下图所示:
接下来最重要的时刻来了,那就是测试了,看看我们的推送能不能成功。
###推送测试
本人是在个推平台上面进行推送测试的,在应用列表里面点击之前创建的应用上的创建推送按钮,如下图所示:
进入后,我有点懵逼,因为之前没有了解过个推SDK的逻辑,在询问了个推技术支持后,技术支持告诉我怎么在个推平台上面去推,也是自己太粗心了,人家进去第一句话就写的很清楚,推送通知目前仅支持安卓用户,iOS请使用透传消息。尴尬!那就透传消息页面试试推。如下图所示:
透传消息测试:
APNs消息测试
NICE啊,这下应用在前台、应用在后台和应用被杀死的情况下都可以收到推送消息了,爽歪歪啊!看来我们已经成功看了百分之九十了!
###Notification Service Extension
正在沾沾自喜的时候,突然发现个推的官网上面还有多媒体推送,我靠,还有这种操作,好奇心的趋势下,让我重新审视如何去做多媒体推送。因为我们之前已经把通知扩展的target创建好了,所以,直接上代码。
1.Notification Service Extension 添加成功后会在项目中自动生成 NotificationService.h 和 NotificationService.m 两个类,包含以下两个方法:
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; // [ 测试代码 ] TODO:用户可以在这里处理通知样式的修改,eg:修改标题,开发阶段可以用于判断是否运行通知扩展 //self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [WillIn]", self.bestAttemptContent.title]; // [ GTSDK ] 统计APNs到达情况和多媒体推送支持接口, 建议使用该接口 [GeTuiExtSdk handelNotificationServiceRequest:request withAttachmentsComplete:^(NSArray *attachments, NSArray *errors) { // self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [Success]", self.bestAttemptContent.title]; self.bestAttemptContent.attachments = attachments; // 设置通知中的多媒体附件 self.contentHandler(self.bestAttemptContent); }]; }
我们可以在这个方法中处理我们的 APNs 通知,并个性化展示给用户。APNs 推送的消息送达时会调用这个方法,此时你可以对推送的内容进行处理,然后使用contentHandler方法结束这次处理。但是如果处理时间过长,将会进入serviceExtensionTimeWillExpire方法进行最后的紧急处理。
- (void)serviceExtensionTimeWillExpire { // [ GTSDK ] 销毁SDK,释放资源 [GeTuiExtSdk destory]; //self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [Timeout]", self.bestAttemptContent.title]; self.contentHandler(self.bestAttemptContent); }
如果didReceiveNotificationRequest方法在限定时间内没有调用 contentHandler方法结束处理,则会在过期之前进行回调本方法。此时你可以对你的 APNs 消息进行紧急处理后展示,如果没有处理,则显示原始 APNs 推送。
接下来就是测试我们的多媒体推送是否成功,我在网上找了个小狗的照片,直接在个推平台上面推。
多媒体测试
6啊,终于搞定了!棒棒哒~~~
但是,之前的一个老项目说也要集成推送,我透,老项目要用XCode集成,这对于我这种懒人来说,简直是一种折磨啊,哎,折磨归折磨,该搞还要搞。但是之前已经用CocoaPods搞过一遍,这次用XCode集成还不是手到擒来。但是为了防止出错,本人还是先做了个demo,这样后面在自己项目上面集成的话把握性会更大,踩的坑也就会更少。
###XCode集成
1.导入个推SDK:
2.库引用检查:
3.添加系统依赖库:
libc++.tbd libz.tbd libsqlite3.tbd libresolv.tbd Security.framework MobileCoreServices.framework SystemConfiguration.framework CoreTelephony.framework AVFoundation.framework CoreLocation.framework UserNotifications.framework (iOS 10 及以上需添加,使用 Optional 方式接入) AdSupport.framework (如果使用无IDFA版本SDK,则需删除该 AdSupport 库)
幸亏后面的步骤基本上都是一样的,唯一的欣慰呀!
4.开启推送功能、后台运行权限设置、开启WiFi信息授权
这里跟上面步骤一样,就不啰嗦了。
5.copy代码,这是我们程序猿最喜欢的啦,哈哈哈,之前搞过,这里就不累赘了。
6.添加Notification Service Extension
(1).打开XCode,菜单中选择File->New->Target->Notification Service Extension。如下图所示:
**注意:**1.Extension的Bundle Identifier不能和Main Target(也就是自己的App Target)的Bundle Identifier相同,否则会报BundleID重复的错误。2.Extension 的 Bundle Identifier 需要在 Main Target 的命名空间下,比如说 Main Target 的 BundleID 为 ent.getui.xxx,那么Extension的BundleID应该类似与ent.getui.xxx.yyy这样的格式。如果不这么做,会引起命名错误。
这个是在个推官网上面看到的,之前自己也踩了这个坑,这里就记录下来。
添加 Notification Service Extension 后会生成相应的 Target。点Finish按钮后会弹出是否激活该 Target 对应 scheme 的选项框,选择 Activate,如果没有弹出该选项框,需要自行添加相应的 scheme。如下图所示:
(2).Notification Service Extension 添加成功后会在项目中自动生成 NotificationService.h 和 NotificationService.m 两个类
这里跟上面一样,就不累赘了。
(3).添加GtExtensionSdk依赖库
选择Notification Service Extension所对应的Target,添加如下依赖库:
libz.tbd libsqlite3.tbd GTExtensionSDK.framework UserNotifications.framework
(4).XCode10建议开启WiFi信息授权:在 Xcode 10.x 以上,找到应用Target设置中的Capabilities -> Access WiFi Information,确认开关已经设为ON状态。如下图所示:
(5).开启多媒体地址Http访问支持:
##集成过程中遇到的问题
无效的deviceToken
最让我印象深刻的就是无效的deviceToken,在测试APNS推送的时候,询问过个推那边的技术支持,他们说可以先在应用配置里面测试一下,然后我就拿着我的deviceToken去测试一下,结果提示我是无效的deviceToken,我晕,然后继续咨询个推的技术支持,他们说这个原因有可能是我证书环境的问题。经过一番仔细的检查之后,发现,我在个推平台上面上传的是通用证书,然后我XCode上面的授权证书是开发环境下,这样一来,拿到的是开发环境下的deviceToken,用测试一下,当然会出错。 解决的方案有两种:第一,在个推开发平台上传开发环境下的推送证书。第二:将自己的授权证书更换为生产环境。
通知扩展里面修改标题的代码不生效
self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [Success]", self.bestAttemptContent.title];
发现demo里面有这么一行代码,我把这行代码打开后,推送一条消息,发现标题没有变化,我惊呆了!询问个推技术支持,个推技术支持说,让我先运行主target,然后再运行通知扩展,运行通知扩展的时候会让我们去找主targetAPP,选择主target,然后再推送就会有了,嗯,想了下,这个应该是XCode的bug。
##结语
最后,我要说,消息推送功能的集成对APP而言真的真的很重要。以上是个推iOS推送SDK集成的全步骤,给大家做个参考。特别需要注意的几点是:
1.在个推平台上上传的推送证书一定要正确并且要和自己的环境相对应,推荐上传P8证书;
2.主target和通知扩展target是两个target,命名和bundleID上要注意,本人是按照个推官网给的建议命名的。