iOS开发-集成个推远程推送

证书配置+个推应用配置略过

Xcode集成

在项目中添加 Notification Service Extension

  • 打开 Xcode 9,菜单中选择 File -> New -> Target -> Notification Service Extension
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这样的格式。如果不这么做,会引起命名错误。

因此我们建议使用

.NotificationService 格式作为Extension的BundleID.

  • 添加 Notification Service Extension 后会生成相应的 Target。点Finish按钮后会弹出是否激活该 Target 对应 scheme 的选项框,选择 Activate,如果没有弹出该选项框,需要自行添加相应的 scheme。如下图:
image
  • 开启多媒体地址 Http 访问支持:

[图片上传失败...(image-e4dca5-1526711478100)]

CocoaPods集成

  #个推
  pod 'GTSDK', '2.0.0.0-noidfa'
  
  target 'NotificationService' do
      platform :ios, "10.0"
      pod 'GTExtensionSDK'
  end

开启推送功能

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

image

后台运行权限设置

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

image

在项目设置中添加以下系统库支持:

libc++.tbd
libz.tbd
libsqlite3.tbd
Security.framework
MobileCoreServices.framework
SystemConfiguration.framework
CoreTelephony.framework
AVFoundation.framework
CoreLocation.framework
UserNotifications.framework (iOS 10 及以上需添加,使用 Optional 方式接入)
AdSupport.framework   (如果使用无IDFA版本SDK,则需删除该 AdSupport 库)

添加 GtExtensionSdk 依赖库

libz.tbd
libsqlite3.tbd
GTExtensionSDK.framework
UserNotifications.framework

NotificationService.m

#import 


- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    [GeTuiExtSdk handelNotificationServiceRequest:request
                          withAttachmentsComplete:^(NSArray *attachments, NSArray *errors) {
                              //TODO:用户可以在这里处理通知样式的修改,eg:修改标题
                              //self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [Success]", self.bestAttemptContent.title];
                              
                              self.bestAttemptContent.attachments = attachments; //设置通知中的多媒体附件
                              NSLog(@"处理个推APNs展示遇到错误:%@", errors);    //如果APNs处理有错误,可以在这里查看相关错误详情
                              
                              self.contentHandler(self.bestAttemptContent); //展示推送的回调处理需要放到个推回执完成的回调中
                              
                              
                          }];
}

- (void)serviceExtensionTimeWillExpire {
    // Called just before the extension will be terminated by the system.
    // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
    //销毁SDK,释放资源
    [GeTuiExtSdk destory];
    self.contentHandler(self.bestAttemptContent);
    
}

AppDelegate

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

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

/// 使用个推回调时,需要添加"GeTuiSdkDelegate"
/// iOS 10 及以上环境,需要添加 UNUserNotificationCenterDelegate 协议,才能使用 UserNotifications.framework 的回调
@interface AppDelegate : UIResponder 
// 用来判断是否是通过点击通知栏开启(唤醒)APP
@property (nonatomic, assign) BOOL isLaunchedByNotification;
  • 初始化SDK并注册APNs
/// 个推开发者网站中申请App时,注册的AppId、AppKey、AppSecret
#define kGtAppId           @"iMahVVxurw6BNr7XSn9EF2"
#define kGtAppKey          @"yIPfqwq6OMAPp6dkqgLpG5"
#define kGtAppSecret       @"G0aBqAD6t79JfzTB6Z5lo5"

    // 通过个推平台分配的appId、 appKey 、appSecret 启动SDK,注:该方法需要在主线程中调用
    dispatch_async(dispatch_get_main_queue(), ^{
        [GeTuiSdk startSdkWithAppId:kGtAppId appKey:kGtAppKey appSecret:kGtAppSecret delegate:self];
    });
    
    // 注册 APNs
    [self registerRemoteNotification];
    
  • 注册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编译会调用
        if (@available(iOS 10.0, *)) {
            UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
            center.delegate = self;
            [center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionCarPlay) completionHandler:^(BOOL granted, NSError *_Nullable error) {
                if (!error) {
                    DLog(@"request authorization succeeded!");
                }
            }];
        } else {
            // Fallback on earlier versions
        }
        
        [[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 {
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
        //写在这个中间的代码,都不会被编译器提示-Wdeprecated-declarations类型的警告
        UIRemoteNotificationType apn_type = (UIRemoteNotificationType)(UIRemoteNotificationTypeAlert |UIRemoteNotificationTypeSound |UIRemoteNotificationTypeBadge);
        [[UIApplication sharedApplication] registerForRemoteNotificationTypes:apn_type];
#pragma clang diagnostic pop
        
    }
}
  • 向个推服务器注册DeviceToken
// 远程通知注册成功委托
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    NSString *token = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    token = [token stringByReplacingOccurrencesOfString:@" " withString:@""];
    DLog(@"\n>>>[DeviceToken Success]:%@\n\n", token);
    // 向个推服务器注册deviceToken
    [GeTuiSdk registerDeviceToken:token];
}
  • 统计APNs通知的点击数

iOS 10 以前,为处理 APNs 通知点击事件,统计有效用户点击数,需在AppDelegate.m里的didReceiveRemoteNotification回调方法中调用个推SDK统计接口:

/** APP已经接收到“远程”通知(推送) - 透传推送消息 {app 在后台没杀死或者杀死 点击通知 首先进入的方法} */
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
    // 处理APNs代码,通过userInfo可以取到推送的信息(包括内容,角标,自定义参数等)。如果需要弹窗等其他操作,则需要自行编码。
    DLog(@"\n>>>[Receive RemoteNotification - Background Fetch]:%@\n\n",userInfo);
    
    self.isLaunchedByNotification = YES;
    //静默推送收到消息后也需要将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  API_AVAILABLE(ios(10.0)){
    
    DLog(@"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 (^)(void))completionHandler  API_AVAILABLE(ios(10.0)){
    DLog(@"didReceiveNotification:%@", response.notification.request.content.userInfo);
    
    self.isLaunchedByNotification = YES;
    // [ GTSdk ]:将收到的APNs信息传给个推统计
    [GeTuiSdk handleRemoteNotification:response.notification.request.content.userInfo];
    completionHandler();
}

#endif
  • 获取CID信息
/** SDK启动成功返回cid */
- (void)GeTuiSdkDidRegisterClient:(NSString *)clientId {
    //个推SDK已注册,返回clientId
    
    DLog(@"-=-=-=----\n>>>[GeTuiSdk RegisterClient]:%@\n\n", clientId);
    
    // 通过接口上传的APP服务器
}
  • 接收个推通道下发的透传消息

SDK 在线时(即 App 在前台运行时)进行消息推送,该消息将直接通过个推通道发送给 App ,通常这种方式比通过APNs发送来得更及时更稳定;当 SDK 离线时(即停止 SDKApp 后台运行 或 App 停止状态时)进行消息推送,个推平台会给苹果 APNs 推送消息,同时保存个推通道的离线消息,当 SDK 重新上线后,个推平台会重新推送所有离线的消息

APP 可以通过[GeTuiSdkDelegate GeTuiSdkDidReceivePayloadData]回调方法获取透传消息,其中payloadData参数为透传消息数据,offLine参数则表明该条消息是否为离线消息。示例代码如下:

/** SDK收到透传消息回调 {app在前台 只走这一个方法}{app在后台,进来前台第二个走的方法} */
- (void)GeTuiSdkDidReceivePayloadData:(NSData *)payloadData andTaskId:(NSString *)taskId andMsgId:(NSString *)msgId andOffLine:(BOOL)offLine fromGtAppId:(NSString *)appId {
    
    [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
    //收到个推消息
    NSString *payloadMsg = nil;
    if (payloadData) {
        payloadMsg = [[NSString alloc] initWithBytes:payloadData.bytes length:payloadData.length encoding:NSUTF8StringEncoding];
    }
    
    NSDictionary *payLoadMegDic = [NSJSONSerialization JSONObjectWithData:payloadData options:NSJSONReadingMutableLeaves error:nil];
    DLog(@"%@",payLoadMegDic);
    if (offLine) {
            if (payLoadMegDic) {
            if (self.isLaunchedByNotification) { // app是通过点击通知栏进入前台
            UITabBarController *_tab = (UITabBarController *)(self.window.rootViewController);
            UINavigationController *nva = (UINavigationController *)_tab.viewControllers[_tab.selectedIndex];
            // 根据payLoadMegDic判断跳转类型
                self.isLaunchedByNotification = NO;
            }else{
                // app是通过点击icon进入前台,在这里不做操作
            }
        }

        NSString *msg = [NSString stringWithFormat:@"taskId=%@,messageId:%@,payloadMsg:%@%@",taskId,msgId, payloadMsg,offLine ? @"<离线消息>" : @""];
        DLog(@"\n>>>[GexinSdk ReceivePayload]:%@\n\n", msg);
        
    }else if(!self.isLaunchedByNotification){
        // app已经处于前台,提示框提示
        // 在这里拿不到apns 里的title body! 如果想要处理的话可以让服务端把aps 的内容传到transmissionContent 透传内容里 ,应用在线的时候可以拿到这个透传
        self.isLaunchedByNotification = NO;
        // 转换成一个本地通知,显示到通知栏,你也可以直接显示出一个alertView,只是那样稍显aggressive
    }
    
    
    
}

你可能感兴趣的:(iOS开发-集成个推远程推送)