美文网首页ios实用开发技巧
iOS开发-集成个推远程推送

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

作者: 看我的大白眼 | 来源:发表于2018-05-19 14:31 被阅读147次

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

    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这样的格式。如果不这么做,会引起命名错误。

    因此我们建议使用<Main Target Bundle ID>.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 <GTExtensionSDK/GeTuiExtSdk.h>
    
    
    - (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 <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>
    // 用来判断是否是通过点击通知栏开启(唤醒)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开发-集成个推远程推送

        本文链接:https://www.haomeiwen.com/subject/mnoodftx.html