iOS开发-极光推送SDK使用笔记

贺景胜
2023-12-01

推送作为一个产品必备功能,越来越得到所有人的重用,市面上做推送的第三方公司也有很多,像个推,百度,甚至友盟也出了推送功能。用什么不是我们能决定的,而是上级或产品决定的,所以我负责的项目用了极光推送,我便来整理一下使用过程和踩了的坑。


开整


准备工作

  • 苹果开发者账号,agent权限,即可以创建和管理证书的权限。
  • 极光开发者账号。
  • 待集成的app项目。

集成前的相关操作


推送证书创建


由于极光后台在创建应用后配置iOS平台推送时需要上传开发环境和生产环境的推送证书,因此,先做这一步。
首先用钥匙串访问app创建CSR文件 (如不会创建自行搜索)
使用苹果开发者账号登录 苹果开发者中心
首先选择创建AppID,填好ID名和bundle ID之后,一定要勾选Push Notifications的选项。
点击创建,之后查看此ID,Push Notifications该选项为黄色,因为没有创建证书。
然后在证书栏选择添加证书,开发和生产的推送证书都申请都要创建:
点击添加证书,开发证书在Development下选择推送选项,生产证书在Prodaction下选择推送选项。
点击继续,这时需要上传之前创建的CSR文件。
创建完成,点击下载。并导入钥匙串访问app。
在钥匙串访问app中将两个证书分别导出,最好起好能分辨的名字,设置密码,备用。 


极光后台应用配置


使用极光后台账号登录 极光后台
点击创建应用,并设置应用名称和应用图标。
创建后,便会获得AppKey。
这时便会到推送设置页面,选择iOS平台,上传上一步导出的对应的开发环境和生产环境的证书。点击上传,配置完毕。 



开始集成

关于集成部分,极光 官方文档极光SDK中已经写的很详细,也可自行翻阅。

SDK获取


还是cocoapods和手动两种导入方式,推荐cocoapods。


Cocoapods导入:
在Podfile中加入:pod ‘JPush’ 并在终端中执行 pod install 命令。

手动导入:
  • 在极光官网下载最新SDK
  • 将SDK包解压,在Xcode中选择“Add files to 'Your project name'...”,将解压后的lib子文件夹(包含JPUSHService.h、jpush-ios-x.x.x.a、jcore-ios-x.x.x.a)添加到你的工程目录中。
  • 添加Framework
    • CFNetwork.framework
    • CoreFoundation.framework
    • CoreTelephony.framework
    • SystemConfiguration.framework
    • CoreGraphics.framework
    • Foundation.framework
    • UIKit.framework
    • Security.framework
    • libz.tbd (Xcode7以下版本是libz.dylib)
    • AdSupport.framework (获取IDFA需要;如果不使用IDFA,请不要添加)
    • UserNotifications.framework (Xcode8及以上)
    • libresolv.tbd (JPush 2.2.0及以上版本需要, Xcode7以下版本是libresolv.dylib)


代码配置


在AppDelegate.m文件中引入以下头文件

 // 引入JPush功能所需头文件
    #import "JPUSHService.h"
    // iOS10注册APNs所需头文件
    #ifdef NSFoundationVersionNumber_iOS_9_x_Max
    #import <UserNotifications/UserNotifications.h>
    #endif
    // 如果需要使用idfa功能所需要引入的头文件(可选)
    #import <AdSupport/AdSupport.h>


然后遵守代理协议 
<JPUSHRegisterDelegate>


首先我们使用推送功能之前都要向苹果APNs服务器注册并获取DeviceToken,因此首先在
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

该方法中写入以下代码
  JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];
  entity.types = JPAuthorizationOptionAlert|JPAuthorizationOptionBadge|JPAuthorizationOptionSound;
  if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
    // 可以添加自定义categories
    // NSSet<UNNotificationCategory *> *categories for iOS10 or later
    // NSSet<UIUserNotificationCategory *> *categories for iOS8 and iOS9
  }
  [JPUSHService registerForRemoteNotificationConfig:entity delegate:self];

注册完成后,在下方实现两个回调方法

/**
 注册成功返回deviceToken 再将deviceToken注测到极光
 */
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    NSString *token = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    token = [token stringByReplacingOccurrencesOfString:@" " withString:@""];
    NSLog(@"\nAPNS注册成功 device token: %@",token);
    /// 极光推送 - 注册 DeviceToken
    [JPUSHService registerDeviceToken:deviceToken];
}

/**
 远程推送注册失败
 */
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
    NSLog(@"\nAPNS注册失败 原因: %@", error);
}

这两个方法是注册APNs后系统的回调方法,用于判断是否注册成功。

然后 我们要初始化JPushSDK,
先在顶部声明几个静态字段
static NSString *appKey = @"你极光后台的appkey”;
static NSString *channel = @"App Store”; //指明应用程序包的下载渠道,为方便分渠道统计,具体值由你自行定义,如:App Store
static BOOL isProduction = FALSE; //证书环境,是否是生产环境证书,需要与运行时的Code Signing中配置的证书环境一致。

然后继续在 didFinishLaunchingWithOptions方法中调用
[JPUSHService setupWithOption:launchOptions appKey:appKey
                        channel:channel
               apsForProduction:isProduction
          advertisingIdentifier:nil];

到此,JPushSDK初始化完成,并启动,如需广告功能请自行查阅文档。

最后,我们想知道推送内容就需要实现对应的代理方法。

首先,下面的方法会在远程通知即将展示时被调用,不论程序是否在前台,但,如果程序在前台,在即将收到通知时,该方法就会被调用,如果程序在后台,那必然不会。
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler {
  // Required
  NSDictionary * userInfo = notification.request.content.userInfo;
  if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
    [JPUSHService handleRemoteNotification:userInfo];
  }
  completionHandler(UNNotificationPresentationOptionAlert); // 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Alert三种类型可以选择设置
}


然后, 下面的方法会在系统下落通知栏被点击时调用,作为相应用户的唯一代理方法。
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
  // Required
  NSDictionary * userInfo = response.notification.request.content.userInfo;
  if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
    [JPUSHService handleRemoteNotification:userInfo];
  }
  completionHandler();  // 系统要求执行这个方法
}

注意,以上的两个代理方法仅在iOS10以上的系统版本会被执行,因为这两个方法是JPush对iOS10中的新推送机制的封装,如果是iOS 10 以前的系统版本,则

iOS 7 以后的通知回调方法:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

  // Required, iOS 7 Support
  [JPUSHService handleRemoteNotification:userInfo];
  completionHandler(UIBackgroundFetchResultNewData);
}

iOS 6之前的通知回调方法:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {

  // Required, iOS6或之前系统
  [JPUSHService handleRemoteNotification:userInfo];
}

此时,推送功能集成完毕,点击编译,如果不报错,恭喜集成成功。

至于收到消息后如何进行处理,请根据自己业务需要,读取userInfo中的对应字段进行处理即可。 

  1. 如果使用的是Xcode8 以上的版本,则需要在Capabilities中,将Push Notification选项打开。之后等待下面两个对号出现。然后将Background Modes打开,勾选最下面两项。
  2. 如果项目不兼容iOS7 之前的版本,那么在注册APNs时无需分版本调用注册方法,上面的注册方法已经兼容了所有版本,如果强行调用以前的方法,那么iOS 9 会出问题。什么问题呢,就是当你的app在一台设备上安装两次或以上时,这时你发一条推送,设备会弹出两条提示。这是为什么呢,首先这是苹果遗留的坑,当iOS 9的设备去注册DeviceToken时,并不是每次都一样,而是当app重新被安装到手机后,这个DeviceToken是会变的,但是呢,旧的DeviceToken仍旧有效,因此当推送传到苹果的APNs服务器时,服务器会找到当前的DeviceToken和之前的DeviceToken(因为都是该设备注册的),这时APNs会向每个DeviceToken都推送一个通知,但设备一样,因此会连续收到两条。但极光SDK在新的接口中已修复了该问题,所以不用区别系统版本去注册APNs。
  3. 一般我们都会根据收到的通知消息携带的数据进行相应处理,但这时一定要进行数据类型判断,一定要进行数据类型判断,一定要进行数据类型判断(重要的事说三遍)。因为我们在日常研发中一般是不会直接使用极光后台来推送消息,都是需要后台来集成极光后端SDK来做出相应的功能,这就造成了,数据是由后台传给极光服务器,极光再推送给我们。那么一旦数据来源是后台,那么我们就必须做到以不变应万变,判断数据类型,分别处理,防止崩溃。
  4. iOS 10的推送确实强大,强大到应用在前台也能提示通知消息,点击也会触发代理方法,这样就变得非常好处理。但是iOS 10 以前就没那么友好了,其一是程序活跃时收到推送但系统无法提示推送,其二是活跃状态和后台状态收到推送点击进入程序时都触发的是同一个回调方法,即didReceiveRemoteNotification,那么在处理时我们必须加入判断,到底是活跃时收到的,还是后台运行时收到点击才进入前台的。我用的处理方式是,在程序进入后台时储存当前时间,然后当程序恢复前台运行时根据之前的时间算出与当前时间的时间差,并以一个属性或全局变量来接收,再在didReceiveRemoteNotification方法中判断,如果这个时间差变量不为0时,那么就是从后台进入前台的动作,如果为0,则是程序一直保持在前台的状态。
  5. 奇葩的产品需求。由于iOS 10 可以在前台对推送消息进行提示,并可在通知中心保留。那么问题来了,产品经理来跟你说,我需要iOS 10 以前的设备也做到这种功能。如果你的第一想法不是打死他,说明你还是有了些自己想法的。是的,虽然系统无法进行处理,但我们可以自行处理。最完美也最复杂的处理方式为,自己写一个与系统提示框一样的下落弹窗,支持点击和上滑收回取消,在点击事件中处理消息数据,然后在收到的通知的同时给系统发一条本地通知插入通知栏内。结束。
  6. 不要相信所谓网上有些技术贴所说可以通过调用
    - (void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler
    这个方法来处理当程序处于活跃状态时可以出现系统下落提示框,首先,人家在注释里说的很明白// Called when your app has been activated by the user selecting an action from a remote notification. 就是说这个方法会在你的app被用户点击远程通知中的某一个操作唤醒时被调用,也就是说,该方法首先不是来了通知就调用,也不是你点击通知栏将app置入前台时调用,而是,当你的通知中有多个操作,比如锁屏时左滑出现的查看按钮或者输入框,或者其他可操作区域时,进行相关操作后,app被唤醒时才会调用该方法。呵呵哒,没文化真可怕。我也被坑了。
  7. 启动SDK时的isProduction字段一定要与当前Code Signing中的证书环境一致,否则我也不知道会出现什么情况,要么推送不成功,要么推错环境,反正哪一种都不是我们想要的,还是乖乖按照标准写吧。

就这么多了。




 类似资料: