CocoaPods集成

CocoaPods集成

前言

  • 本文档介绍XCode开发环境下手动方式导入SDK资源进行集成的步骤,配置相对复杂,需要仔细阅读文档和Demo工程。
  • 个推在国内首创APNs展示统计功能,可精确统计通过苹果APNs服务下发的通知数量,开发者可以获知更为精准的推送展示数和通知点击率,为运营提供了更强大的数据支持。
  • 个推多媒体推送功能支持,可通过后台推送多媒体资源,支持图片,音频,视频的展示。
  • 本文档适用SDK版本:2.3.0.0及以后
  • 请参考GtSdkDemoDemo工程

1. 创建个推应用

  • 请登录 http://dev.getui.com ,选择登记应用并填写应用名称和包名信息,完成应用创建:

img_xcode_create

img_xcode_create

  • 点击应用配置,获取到相应的AppIDAppKeyAppSecret信息:

img_xcode_create

2. 打开项目工程

  • 启动XCode,打开您之前创建的iOS项目工程:

img_xcode_open

3. 添加个推SDK及相关配置

3.1 个推iOS SDK资料包结构

GETUI_IOS_SDK/
  |- readme.txt (SDK资料包说明)
  |- API文档/ (iOS SDK相关集成文档PDF版本)
  |- 资源文件/
  |    |- GtSdkLib/ (标准版个推SDK.framework文件)
  |    |   |- GTExtensionSDK.framework
  |    |   |- GTSDK.framework
  |    |- GtSdkLib-noidfa/ (无IDFA版个推SDK.framework文件)
  | - Demo工程/
  |    |- GtSdkDemo/ (XCode标准集成演示Demo工程)
  |    |- GtSdkDemo-objc/ (Object-C标准集成框架代码工程,示范CocoaPods集成)
  |    |- GtSdkDemo-swift/ (Swift标准集成框架代码工程)
  |    |- GtSdkLib/ (个推SDK库文件,供上述Demo工程引用)
  • GtSdkDemo:SDK 演示 Demo,能更好的展示个推 SDK 功能点。
  • GtSdkDemo-objc:objc 集成 Demo,方便 objc 开发者集成个推 SDK。
  • GtSdkDemo-swift: Swift 集成 Demo,方便 Swift 开发者集成个推 SDK。

3.2 CocoaPods导入

3.2.1 安装 CocoaPods

安装方式简单, Mac 下都自带 ruby,使用 ruby 的 gem 命令即可下载安装:

$ sudo gem install cocoapods
$ pod setup

3.2.2 准备Podfile

使用时需要新建一个名为 Podfile 的文件,如下格式,将依赖的库名字依次列在文件中即可:

  • 如果使用标准版本:
target 'GtSdkDemo-objc' do
    platform :ios, "7.0"
    pod 'GTSDK'
end

target 'NotificationService' do
    platform :ios, "10.0"
    pod 'GTExtensionSDK'
end

注意事项:

(1)、在 App 内投放广告,获取 IDFA 可通过苹果审核。

(2)、App 内无广告,但由于先前投放的特定广告,可参考如下勾选,通过苹果审核。

需勾选如图:

  • 如果使用无 IFDA 版本:

target 'GtSdkDemo-objc' do
    platform :ios, "7.0"
    pod 'GTSDK', '2.3.0.0-noidfa'
end

target 'NotificationService' do
    platform :ios, "10.0"
    pod 'GTExtensionSDK'
end


注意事项:

  1. pod 'GTSDK' 为添加在主 target 上, pod 'GTExtensionSDK' 为添加在 NotificationService Target 上。

  2. 在 App 内无广告情况下还是建议开发者使用获取 IDFA 版本,并参考(2)中所说的方式提交 AppStore 审核。当然,如果开发者不想使用 IDFA 或者担忧采集 IDFA 而未集成任何广告服务遭到 Apple 拒绝,我们也准备了该无 IDFA 版本供开发者集成。

  3. pod 'GTExtensionSDK' 库需提前添加 Notification Service Extension, 添加方式参考【6. iOS 多媒体及展示统计】。

3.2.3 完成 GTSDK 导入

  • 将编辑好的 Podfile 文件放到你的项目根目录中,执行如下命令即可:
    $ cd "<path/to/project>"
    $ pod install
    


注意事项:

1.如果出现错误 :【Unable to find a target named 'NitificationService', did find 'cocoapos'】,为工程中未添加 Notification Service Extension 导致,需手动添加 NitificationService,添加方式参考【6. iOS 多媒体及展示统计】。

3.3 开启推送功能

  • 在 Xcode 8.x 以上,必须开启Push Notification能力。找到应用Target设置中的Capabilities -> Push Notifications,确认开关已经设为ON状态。如果没有开启该开关,在 Xcode 8.x 上编译后的应用将获取不到DeviceToken

3.4 后台运行权限设置

  • 为了更好支持消息推送,提供更多的推送样式,提高消息到达率,需要配置后台运行权限:

  • Remote notifications:APNs静默推送权限

4. 编写集成代码

4.1 在 AppDelegate 中实现个推回调接口

  • AppDelegate增加回调接口类。在iOS 10以前的设备,回调事件通过GeTuiSdkDelegate来进行,在 iOS 10以后,可以使用UserNotifications 框架来实现。示例代码如下:
// AppDelegate.h

#import <UIKit/UIKit.h>
#import <GTSDK/GeTuiSdk.h>     // GetuiSdk头文件应用

// iOS10 及以上需导入 UserNotifications.framework
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
#import <UserNotifications/UserNotifications.h>
#endif

/// 使用个推回调时,需要添加"GeTuiSdkDelegate"
/// iOS 10 及以上环境,需要添加 UNUserNotificationCenterDelegate 协议,才能使用 UserNotifications.framework 的回调
@interface AppDelegate : UIResponder <UIApplicationDelegate, GeTuiSdkDelegate, UNUserNotificationCenterDelegate>

4.2 初始化SDK并注册APNs

  • [AppDelegate didFinishLaunchingWithOptions]方法中调用个推sdk初始化方法,传入个推平台分配的 AppIDAppKeyAppSecret。同时,调用APNs注册方法,尝试获取APNs DeviceToken。示例代码如下:
/// 个推开发者网站中申请App时,注册的AppId、AppKey、AppSecret
#define kGtAppId           @"iMahVVxurw6BNr7XSn9EF2"
#define kGtAppKey          @"yIPfqwq6OMAPp6dkqgLpG5"
#define kGtAppSecret       @"G0aBqAD6t79JfzTB6Z5lo5"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 通过个推平台分配的appId、 appKey 、appSecret 启动SDK,注:该方法需要在主线程中调用
    [GeTuiSdk startSdkWithAppId:kGtAppId appKey:kGtAppKey appSecret:kGtAppSecret delegate:self];
    // 注册 APNs
    [self registerRemoteNotification];
    return YES;
}
  • 注册APNs获取DeviceToken的流程,根据项目设置的不同以及手机系统版本的不同,注册代码会有所区别,可以参考如下方式进行适配:
/** 注册 APNs */
- (void)registerRemoteNotification {
    /*
     警告:Xcode8 需要手动开启"TARGETS -> Capabilities -> Push Notifications"
     */

    /*
     警告:该方法需要开发者自定义,以下代码根据 APP 支持的 iOS 系统不同,代码可以对应修改。
     以下为演示代码,注意根据实际需要修改,注意测试支持的 iOS 系统都能获取到 DeviceToken
     */
    if ([[UIDevice currentDevice].systemVersion floatValue] >= 10.0) {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 // Xcode 8编译会调用
        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];
#else // Xcode 7编译会调用
        UIUserNotificationType types = (UIUserNotificationTypeAlert | UIUserNotificationTypeSound | UIUserNotificationTypeBadge);
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
        [[UIApplication sharedApplication] registerForRemoteNotifications];
        [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
#endif
    } else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
        UIUserNotificationType types = (UIUserNotificationTypeAlert | UIUserNotificationTypeSound | UIUserNotificationTypeBadge);
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
        [[UIApplication sharedApplication] registerForRemoteNotifications];
        [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
    } else {
        UIRemoteNotificationType apn_type = (UIRemoteNotificationType)(UIRemoteNotificationTypeAlert |
                                                                       UIRemoteNotificationTypeSound |
                                                                       UIRemoteNotificationTypeBadge);
        [[UIApplication sharedApplication] registerForRemoteNotificationTypes:apn_type];
    }
}

4.3 向个推服务器注册DeviceToken

  • 为了免除开发者维护DeviceToken的麻烦,个推SDK可以帮开发者管理好这些繁琐的事务。应用开发者只需调用个推SDK的接口汇报最新的DeviceToken,即可通过个推平台推送 APNs 消息。示例代码如下:
/** 远程通知注册成功委托 */
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    NSString *token = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    token = [token stringByReplacingOccurrencesOfString:@" " withString:@""];
    NSLog(@"\n>>>[DeviceToken Success]:%@\n\n", token);

    // 向个推服务器注册deviceToken
    [GeTuiSdk registerDeviceToken:token];
}

4.4 统计APNs通知的点击数

  • 在iOS 10 以前,为处理 APNs 通知点击事件,统计有效用户点击数,需在AppDelegate.m里的didReceiveRemoteNotification回调方法中调用个推SDK统计接口:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    // 将收到的APNs信息传给个推统计
    [GeTuiSdk handleRemoteNotification:userInfo];
    completionHandler(UIBackgroundFetchResultNewData);
}
  • 对于iOS 10 及以后版本,为处理 APNs 通知点击,统计有效用户点击数,需先添加 UNUserNotificationCenterDelegate,然后在AppDelegate.mdidReceiveNotificationResponse回调方法中调用个推SDK统计接口:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0

//  iOS 10: App在前台获取到通知
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {

    NSLog(@"willPresentNotification:%@", notification.request.content.userInfo);

    // 根据APP需要,判断是否要提示用户Badge、Sound、Alert
    completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
}

//  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();
}

#endif

4.5 获取CID信息

  • 个推SDK初始化完成后,可以在[GeTuiSdkDelegate GeTuiSdkDidRegisterClient]回调方法中获取注册成功的ClientID(即CID):
/** SDK启动成功返回cid */
- (void)GeTuiSdkDidRegisterClient:(NSString *)clientId {
    //个推SDK已注册,返回clientId
    NSLog(@"\n>>>[GeTuiSdk RegisterClient]:%@\n\n", clientId);
}

5. 个推高级功能

5.1 接收个推通道下发的透传消息

  • 当 SDK 在线时(即 App 在前台运行时)进行消息推送,该消息将直接通过个推通道发送给 App ,通常这种方式比通过APNs发送来得更及时更稳定;当 SDK 离线时(即停止 SDK 或 App 后台运行 或 App 停止状态时)进行消息推送,个推平台会给苹果 APNs 推送消息,同时保存个推通道的离线消息,当 SDK 重新上线后,个推平台会重新推送所有离线的消息。
  • APP 可以通过[GeTuiSdkDelegate GeTuiSdkDidReceivePayloadData]回调方法获取透传消息,其中payloadData参数为透传消息数据,offLine参数则表明该条消息是否为离线消息。示例代码如下:
/** 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);
}


上述接口获取的透传消息为下图中消息内容输入框中的内容:

xcode_6

5.2 苹果 APNs 静默推送

  • 如果需要使用 静默推送(Remote Notifications)功能,请在推送时指定content-available:1参数。
  • APP 可以通过[AppDelegate didReceiveRemoteNotification]回调方法获取APNs 远程通知数据。
  • 静默推送收到消息后也需要将APNs信息传给个推统计,添加[GeTuiSdk handleRemoteNotification:userInfo];消息回执。
  • 示例代码如下:
/** APP已经接收到“远程”通知(推送) - 透传推送消息  */
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
    // 处理APNs代码,通过userInfo可以取到推送的信息(包括内容,角标,自定义参数等)。如果需要弹窗等其他操作,则需要自行编码。
    NSLog(@"\n>>>[Receive RemoteNotification - Background Fetch]:%@\n\n",userInfo);

    //静默推送收到消息后也需要将APNs信息传给个推统计
    [GeTuiSdk handleRemoteNotification:userInfo];

    completionHandler(UIBackgroundFetchResultNewData);
 }


请参照【3.5】节的内容正确添加Remote notifications权限

5.3 指定标签推送

  • 应用可以为设备设置一组自定义标签,后续可以针对某一特定标签用户组进行推送:
NSString *tagName = @"个推,推送,iOS";
NSArray *tagNames = [tagName componentsSeparatedByString:@","];
if (![GeTuiSdk setTags:tagNames]) {
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Failed" message:@"设置失败" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alertView show];
}

5.4 设置别名

  • 应用可以为设备设置不同的别名,后续可以针对具体别名进行推送:
// 绑定别名
[GeTuiSdk bindAlias:@"个推" andSequenceNum:@"seq-1"];
// 取消绑定别名
[GeTuiSdk unbindAlias:@"个推" andSequenceNum:@"seq-2" andIsSelf:YES];
  • 解绑说明:解绑别名中,isSelf 字段为标识是否只对当前 cid 生效,如果是 true,只对当前 cid 做解绑;如果是 false,对所有绑定该别名的 cid 列表做解绑。

  • 可以在GeTuiSdkDidAliasAction方法中处理 绑定/解绑 响应:

- (void)GeTuiSdkDidAliasAction:(NSString *)action result:(BOOL)isSuccess sequenceNum:(NSString *)aSn error:(NSError *)aError {
    if ([kGtResponseBindType isEqualToString:action]) {
        NSLog(@"绑定结果 :%@ !, sn : %@", isSuccess ? @"成功" : @"失败", aSn);
        if (!isSuccess) {
            NSLog(@"失败原因: %@", aError);
        }
    } else if ([kGtResponseUnBindType isEqualToString:action]) {
        NSLog(@"绑定结果 :%@ !, sn : %@", isSuccess ? @"成功" : @"失败", aSn);
        if (!isSuccess) {
            NSLog(@"失败原因: %@", aError);
        }
    }
}

5.5 设置角标

  • badge是 iOS 用来标记应用程序状态的一个数字,出现在程序图标右上角。个推SDK封装badge功能,允许应用上传badge值至个推服务器,由个推后台帮助开发者管理每个用户所对应的推送badge值,简化了设置推送badge的操作。
  • 实际应用中,开发者只需将变化后的Badge值通过setBadge接口同步给个推服务器即可,无需自己维护用户与badge值之间的对应关系,方便运营维护。
  • 注意:该接口只是将个推服务器中保存的Badge值进行修改,不会修改App图标右上角显示的Badge值,App显示的Badge值需要开发者根据需求自行修改,如下面的事例代码。

支持版本: v1.4.1及后续版本

[GeTuiSdk setBadge:badge]; //同步本地角标值到服务器
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge]; //APP 显示角标需开发者调用系统方法进行设置

5.6 重置角标

  • 重置角标, 重置服务器角标计数,使服务端计数变更为0。
  • 注意:该接口只是将个推服务器中保存的Badge值进行重置,不会修改App图标右上角显示的Badge值,App显示的Badge值需要开发者根据需求自行修改,如下面的事例代码。

支持版本: v1.4.1及后续版本

[GeTuiSdk resetBadge]; //重置角标计数
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; // APP 清空角标

5.7 设置渠道标识

  • 设置渠道信息,便于后续根据渠道各项统计信息,详情请咨询技术支持人员。

支持版本: v1.5.0及后续版本

[GeTuiSdk setChannelId:@"GT-Channel"];

6. iOS 多媒体及展示统计

  • Apple 在 iOS 10 中新增了Notification Service Extension机制,可在消息送达时进行业务处理。为精确统计消息送达率,在集成个推SDK时,可以添加 Notification Service Extension,并在 Extensions 中添加 GtExtensionSdk 的统计接口,实现消息展示回执统计功能。

6.1 在项目中添加 Notification Service Extension

  • 打开 Xcode 8,菜单中选择 File -> New -> Target -> Notification Service Extension

填写Target信息时需要注意以下两点:

  • Extension 的 Bundle Identifier 不能和 Main Target(也就是你自己的 App Target)的 Bundle Identifier 相同,否则会报 BundeID 重复的错误。
  • Extension 的 Bundle Identifier 需要在 Main Target 的命名空间下,比如说 Main Target 的 BundleID 为 ent.getui.xxx,那么Extension的BundleID应该类似与ent.getui.xxx.yyy这样的格式。如果不这么做,会引起命名错误。

因此我们建议使用<Main Target Bundle ID>.NotificationService格式作为Extension的BundleID。

  • 添加 Notification Service Extension 后会生成相应的 Target。点Finish按钮后会弹出是否激活该 Target 对应 scheme 的选项框,选择 Activate,如果没有弹出该选项框,需要自行添加相应的 scheme。如下图:

  • Notification Service Extension 添加成功后会在项目中自动生成 NotificationService.hNotificationService.m 两个类,包含以下两个方法:
didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent *contentToDeliver))contentHandler
  • 我们可以在这个方法中处理我们的 APNs 通知,并个性化展示给用户。APNs 推送的消息送达时会调用这个方法,此时你可以对推送的内容进行处理,然后使用contentHandler方法结束这次处理。但是如果处理时间过长,将会进入serviceExtensionTimeWillExpire方法进行最后的紧急处理。
serviceExtensionTimeWillExpire
  • 如果didReceiveNotificationRequest方法在限定时间内没有调用 contentHandler方法结束处理,则会在过期之前进行回调本方法。此时你可以对你的 APNs 消息进行紧急处理后展示,如果没有处理,则显示原始 APNs 推送。

6.2 使用 GtExtensionSdk 进行 APNs 展示统计

  • NotificationService.m 文件中,导入 GtExtensionSdk 的头文件:
#import <GTExtensionSDK/GeTuiExtSdk.h>
  • NotificationService.m中有两个回调方法 didReceiveNotificationRequestserviceExtensionTimeWillExpire。需要在 didReceiveNotificationRequest 中添加 GtExtensionSdk 的推送回执统计处理,由于回执操作是异步请求,因此待展示推送的回调处理需要放到回执完成的回调中。示例代码如下:
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {

    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];

     //使用 handelNotificationServiceRequest 上报推送送达数据。
    [GeTuiExtSdk handelNotificationServiceRequest:request withComplete:^{

    //注意:是否修改下发后的title内容以项目实际需求而定
    //self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [需求而定]", self.bestAttemptContent.title];

     // 待展示推送的回调处理需要放到回执完成的回调中
     self.contentHandler(self.bestAttemptContent);
    }];
}
  • 我们对这个统计处理过程限定必须在5s内完成,因此开发者无需担心因为这个统计处理影响到消息的正常展示。

6.3 使用 GtExtensionSdk 进行多媒体推送及展示统计(建议使用该方法)

  • 1.引用:在 NotificationService.m 文件中,导入 GtExtensionSdk 的头文件:
#import <GTExtensionSDK/GeTuiExtSdk.h>
  • 2.查看:点击 GeTuiExtSdk 头文件,查看有两个方法,如下:
/**
 *  统计APNs到达情况
 */
+ (void)handelNotificationServiceRequest:(UNNotificationRequest *) request withComplete:(void (^)(void))completeBlock;

/**
 *  统计APNs到达情况和多媒体推送支持,建议使用该方法接入
 */
+ (void)handelNotificationServiceRequest:(UNNotificationRequest *)request withAttachmentsComplete:(void (^)(NSArray* attachments, NSArray* errors))completeBlock;

/**
 *  sdk 销毁,资源释放
 */
+ (void)destory;

接口使用说明:

1).如果需要多媒体推送,请使用第二种方法进行调用。接收到 APNs 通知后,SDK 判断是否有多媒体资源,如果多媒体资源存在则SDK下载资源,下载完成后以 Block 方式回调返回 attachments 资源数组对象和 errors 错误数组信息。

2).多媒体大小限制:

资源 类型 大小 超时
图片 1 <=10M 120s
音频 2 <=5M 60s
视频 3 <=50 180s

3).可设置是否仅在 WIFI 下下载资源,参考服务端API。如果为True,数据网络环境下将不下载资源,展示通知不带附件。默认是:False,支持 WIFI 和数据网络下下载。

4). 资源URL格式注意:不支持中文及转译地址,请使用英文合法地址。

  • 3.接入:NotificationService.m中有两个回调方法 didReceiveNotificationRequestserviceExtensionTimeWillExpire。需要在 didReceiveNotificationRequest 中添加 GtExtensionSdk 的推送回执统计处理,由于回执操作是异步请求,因此待展示推送的回调处理需要放到回执完成的回调中。示例代码如下:
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {

    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];

    NSLog(@"----将APNs信息交由个推处理----");

    [GeTuiExtSdk handelNotificationServiceRequest:request withAttachmentsComplete:^(NSArray *attachments, NSArray* errors) {

        //注意:是否修改下发后的title内容以项目实际需求而定
        //self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [需求而定]", self.bestAttemptContent.title];

        self.bestAttemptContent.attachments = attachments; //设置通知中的多媒体附件

        NSLog(@"个推处理APNs消息遇到错误:%@",errors); //如果APNs处理有错误,可以在这里查看相关错误详情

        self.contentHandler(self.bestAttemptContent); //展示推送的回调处理需要放到个推回执完成的回调中
    }];
}
  • 4.错误分析:多附件下载时每个附件遇到错误都将以 NSError* 对象返回错误信息。当多媒体无法展示时,请注意检查 NSError 中错误信息。
ErrorCode 描述 解决
10001 多媒体地址错误,无法下载 请使用正常地址下发
10002 Http URL 不支持 支持 ATS 使用 Https 地址或者将 NotificationService 配置为支持 Http 请求,如下图所示
10003 多媒体资源超过大小限制 请检查多媒体资源大小是否符合,更正后重新下发

开启多媒体地址 Http 访问支持:

6.4 超时资源释放

  • NotificationService 处理超时时,需调用 GtExtensionSdk 中 destory 方法,进行资源释放,防止因为资源未正常释放导致 crash。
//NotificationService处理超时时系统调用
- (void)serviceExtensionTimeWillExpire { 

    //销毁SDK,释放资源
    [GeTuiExtSdk destory];
    //结束 NotificationService 调用
    self.contentHandler(self.bestAttemptContent);
}

6.5 带 Notification Service Extension 项目上传 AppStore 须知

  • 使用了 Notification Service Extension 的项目在制作待上传至 AppStore 的 IPA 包时,编译设备需要选择 Generic iOS Device,然后再进行 Product -> Archive 操作。只有选择 Generic iOS Device 才能打包编译出带有 Notification Service Extension 且适配全机型设备的 IPA 包。如下图所示:

7. VOIP 推送

  • Apple 在 iOS 8 后引入了一个基于 PushKit 框架的 VoIP 推送,这可以使用户收到一个 VoIP 推送时唤醒 APP。有了这种 VoIP 的推送,开发者们不需要让 APP 持续保持后台运行。当 APP 在后台被系统挂起后如果需要 APP 后台运行,可以发送这个 VoIP 消息将会在后台唤醒 APP,并执行制定的代码 (在弹出通知消息给用户之前)。

  • VoIP 服务需系统 IOS8 及以上系统支持。

  • VoIP 证书不区分开发和生产环境,VoIP 证书只有一个,生产和开发都可用同一个证书。

7.1 添加 PushKit.Framework 库

1、导入 PushKit.Framework 库以 Optional 方式引入。

2、导入头文件 #import <PushKit/PushKit.h>

3、AppDelegate 实现 PKPushRegistryDelegate 协议,并实现 Delegate 中的接口。


- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type {
<!--具体实现参考 7.2 -->
}

- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
<!--具体实现参考 7.3 -->
}

7.2 添加 VOIP 权限

在 info.plist 中添加 voip 权限,如图:

7.3 注册 VOIP Token

1、注册 VOIP

  • 实现 VIOP 注册
//注册VOIP
- (void)voipRegistration {
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    PKPushRegistry *voipRegistry = [[PKPushRegistry alloc] initWithQueue:mainQueue];
    voipRegistry.delegate = self;
    voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
}
  • APPDelegate 的 didFinishLaunchingWithOptions 中调用 VOIP 注册
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [GeTuiSdk runBackgroundEnable:YES];           // [是否允许APP后台运行]
    [GeTuiSdk lbsLocationEnable:YES andUserVerify:YES]; // [是否运行电子围栏Lbs功能和是否SDK主动请求用户定位]
    [GeTuiSdk setChannelId:@"GT-Channel"];              // 渠道
    // [1]:使用APPID/APPKEY/APPSECRENT创建个推实例
    [GeTuiSdk startSdkWithAppId:[AppConfig GtAppId] appKey:[AppConfig GtAppKey] appSecret:[AppConfig GtAppSecret] delegate:self];

    // [2]:注册APNS
    [self registerRemoteNotification];

    // [3]:注册Voip
    [self voipRegistration];

    return YES;
}

2、绑定 VOIP Token

- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type {
    NSString *voiptoken = [credentials.token.description stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    voiptoken = [voiptoken stringByReplacingOccurrencesOfString:@" " withString:@""];

    // 向个推服务器注册 VoipToken
    [GeTuiSdk registerVoipToken:voiptoken];

    NSLog(@"[VoipToken is:]:%@", voiptoken);
}

7.4 接收 VOIP 推送处理具体业务逻辑,并调用个推VOIP回执统计,统计 VOIP 到达情况。

- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
    //3-1: TODO:接收 VOIP 推送中的 Payload 信息进行业务处理。
    <!--TODO: 具体 VOIP 业务处理-->
    NSLog(@"[Voip Payload]:%@,%@", payload, payload.dictionaryPayload);

    //3-2:调用个推 VOIP 回执统计接口
    [GeTuiSdk handleVoipNotification:payload.dictionaryPayload];
}

9. AppLink 支持

AppLink 接入文档

10.Pod 接入问题

10.1 GTSDK 和 GTExtensionSDK 类冲突问题

如下图所示,如果出现 GTSDK 和 GTExtensionSDK 两个库中类冲突问题,原因是因为 Pod 工程配置时将两个库合成到同一个Target上,导致两个库冲突。而个推接入中两个库分别独立使用,GTSDK 在主 Target 上使用,GTExtensionSDK 在 NotificationService Target 上使用,不需要也不能将两个库一起引用到同一个 Target 上。

解决方案:

如下图,需要手动修改 pod 的 xcconfig 文件(debug 和release都需要),在主 Target 中 删除 GTExtensionSDK 库的引用。如图,标注的地方删掉即可。

10. 推送流程

  • iOS应用、应用服务器、个推SDK、个推服务器、Apple Push Notification Server之间的交互流程图,如下图所示:
    如图编译时

  • 尤其需要注意在SDK在线和离线状态下分别不同的处理流程,APP 需要很好理解不同场景下的推送消息处理流程。

11. CocoaPods 的安装和使用介绍

11.1 介绍

每种语言发展到一个阶段,就会出现相应的依赖管理工具,例如 Java 语言的 Maven,nodejs 的 npm。随着 iOS 开发者的增多,业界也出现了为 iOS 程序提供依赖管理的工具,它的名字叫做:CocoaPods。
CocoaPods 项目的源码 在 Github 上管理。该项目开始于 2011 年 8 月 12 日,经过多年发展,现在已经成为 iOS 开发事实上的依赖管理标准工具。开发 iOS 项目不可避免地要使用第三方开源库,CocoaPods 的出现使得我们可以节省设置和更新第三方开源库的时间。

在没有使用 CocoaPods 以前,我需要:
把这些第三方开源库 的源代码文件复制到项目中,或者设置成 git 的 submodule。 对于这些开源库通常需要依赖系统的一些 framework,我需要手工地将这些 framework 分别增加到项目依赖中,比如通常情况下,一个网络库就需要增加以下 framework: CFNetwork, SystemConfiguration, MobileCoreServices, CoreGraphics, zlib。 对于某些开源库,我还需要设置-licucore或者 -fno-objc-arc等编译参数 管理这些依赖包的更新。 这些体力活虽然简单,但毫无技术含量并且浪费时间。在使用 CocoaPods 之后,我只需要将用到的第三方开源库放到一个名为 Podfile 的文件中,然后执行pod install。 CocoaPods 就会自动将这些第三方开源库的源码下载下来,并且为我的工程设置好相应的系统依赖和编译参数。

11.2 安装

安装方式异常简单 , Mac 下都自带 ruby,使用 ruby 的 gem 命令即可下载安装:

$ sudo gem install cocoapods
$ pod setup

如果你的 gem 太老,可能也会有问题,可以尝试用如下命令升级 gem:

sudo gem update --system

另外,ruby 的软件源[ https://rubygems.org] 因为使用的是亚马逊的云服务,所以被墙了,需要更新一下 ruby 的源,使用如下代码将官方的 ruby 源替换成国内淘宝的源:

gem sources --remove https://rubygems.org/
gem sources -a http://ruby.taobao.org/
gem sources -l

还有一点需要注意,pod setup在执行时,会输出Setting up CocoaPods master repo,但是会等待比较久的时间。这步其实是 Cocoapods 在将它的信息下载到 ~/.cocoapods目录下,如果你等太久,可以试着 cd 到那个目录,用du -sh *来查看下载进度。你也可以参考本文接下来的使用 cocoapods 的镜像索引一节的内容来提高下载速度。

使用 CocoaPods 的镜像索引:

所有的项目的 Podspec 文件都托管在[https://github.com/CocoaPods/Specs]。第一次执行pod setup时,CocoaPods 会将这些podspec索引文件更新到本地的 ~/.cocoapods/目录下,这个索引文件比较大,有 80M 左右。所以第一次更新时非常慢。

有人在 gitcafe 和 oschina 上建立了 CocoaPods 索引库的镜像,因为 gitcafe 和 oschina 都是国内的服务器,所以在执行索引更新操作时,会快很多。如下操作可以将 CocoaPods 设置成使用 gitcafe 镜像:

pod repo remove master
pod repo add master https://gitcafe.com/akuandev/Specs.git
pod repo update

将以上代码中的 [https://gitcafe.com/akuandev/Specs.git] 替换成 [http://git.oschina.net/akuandev/Specs.git] 即可使用 oschina 上的镜像。

11.3 使用 CocoaPods

使用时需要新建一个名为 Podfile 的文件,以如下格式,将依赖的库名字依次列在文件中即可

platform :ios
pod 'JSONKit',       '~> 1.4'
pod 'Reachability',  '~> 3.0.0'
pod 'ASIHTTPRequest'
pod 'RegexKitLite'

然后你将编辑好的 Podfile 文件放到你的项目根目录中,执行如下命令即可:

cd "your project home"
pod install

现在,你的所有第三方库都已经下载完成并且设置好了编译参数和依赖,你只需要记住如下 2 点即可:
使用 CocoaPods 生成的 .xcworkspace 文件来打开工程,而不是以前的 .xcodeproj 文件。 每次更改了 Podfile 文件,你需要重新执行一次pod update命令。 查找第三方库
你如果不知道 cocoaPods 管理的库中,是否有你想要的库,那么你可以通过 pod search 命令进行查找,以下是我用 pod search json 查找到的所有可用的库:

$ pod search json
-> AnyJSON (0.0.1)
   Encode / Decode JSON by any means possible.
   - Homepage: https://github.com/mattt/AnyJSON
   - Source:   https://github.com/mattt/AnyJSON.git
   - Versions: 0.0.1 [master repo]
-> JSONKit (1.5pre)
   A Very High Performance Objective-C JSON Library.
   - Homepage: https://github.com/johnezang/JSONKit
   - Source:   git://github.com/johnezang/JSONKit.git
   - Versions: 1.5pre, 1.4 [master repo]
// ... 以下省略若干行

关于 Podfile.lock

当 你执行pod install之后,除了 Podfile 外,CocoaPods 还会生成一个名为Podfile.lock的文件,Podfile.lock 应该加入到版本控制里面,不应该把这个文件加入到.gitignore中。因为Podfile.lock会锁定当前各依赖库的版本,之后如果多次执行 pod install 不会更改版本,要pod update才会改Podfile.lock了。这样多人协作的时候,可以防止第三方库升级时造成大家各自的第三方库版本不一致。
CocoaPods 的这篇 官方文档 也在What is a Podfile.lock一节中介绍了Podfile.lock的作用,并且指出:
This file should always be kept under version control.

11.4 更新 Pod

pod install 
pod update

11.5 建立私有仓库

11.5.1 创建代码仓库

将自己写的代码推送到git服务器。如果代码可以开源的话,可以用github来托管。参考[个推cocoapods]。

11.5.2 给稳定的代码打上版本tag,一般以版本号作为tag名

git tag -a1.4.1’ -m ‘version 1.4.1

将tag推送到git服务器

git push --tags

11.5.3 创建spec文件

pod spec create https://github.com/GetuiLaboratory/getui-sdk-ios-cocoapods.git

会在当前目录创建.podspec文件,创建的文件是个完整的配置模板,根据字面意思以及注释,大体上都能弄明白。不清楚的地方可以[参考],github上也有很多开源代码可以参考。

11.5.4 验证spec文件有效性

pod spec lint .podspec

11.5.5 创建spec repository(spec 仓库)

├─Specs  
    ├──getui-sdk-ios-cocoapods/  
    ├── 1.4.1
            └── getui-sdk-ios-cocoapods.podspec

这里的版本号要和代码仓库里的tag一一对应 [参考]

11.5.6 添加私有repo到CocoaPods中

pod repo add GetuiLaboratory https://github.com/GetuiLaboratory/Specs.git

11.5.7 验证私有repo安装无误

$ cd ~/.cocoapods/repos/REPO_NAME 
$ pod repo lint .

后面如果还要往REPO_NAME里添加新包,只需运行下面命令:

pod repo push REPO_NAME SPEC_NAME.podspec

如要删除私有repo:

pod repo remove [name]

11.5.8 添加包到工程的Podfile中如下:

source 'https://github.com/GetuiLaboratory/Specs.git'
source 'https://github.com/CocoaPods/Specs.git'

platform :ios, "7.0"
pod 'Reachability'
pod 'GTSDK'

11.5.9 运行

到工程目录下运行

pod update
通知
2018.06.19 Android SDK 2.12.4.0

增加Applink点击回执接口 修复若干其他bug以及性能优化

......
2018.06.19 iOS SDK 2.3.0.0

新增 AppLink 服务及统计支持。 新增短信补量推送服务。

......
2018.03.21 Android SDK 4.3.0.0

增加通知到达、点击回调 适配Android 8.0 修复若干其他bug

......
2018.03.19 Android SDK 2.12.3.0

增加通知到达、点击回调 适配Android 8.0 修复若干其他bug

......
2018.01.29 JAVA SDK 4.0.1.17

苹果消息推送添加语音功能(VOIP)

......

文档中心搜索