个推使用指南

简介: 个推使用指南

按照个推文档CocoaPods集成,基本能实现个推的基本推送功能。但是很多细节文档没有说,我就走了很多弯路。

第一步:首先登录个推网站,注册用户并登录。

第二步:在个推·消息推送登记应用。

要登记测试环境和生产环境两个应用。

第三步:点击应用配置,显示上图页面,上传或修改开发环境的p12正书。设置证书密码,这个密码要和p12证书(《制作p12证书》)的密码一致。然后点击测试一下。

注意:证书测试就是apns测试,走的是苹果推送服务器。

没有什么好说的,运行你集成了个推的应用,把打印deviceToken输入对话框就能测试apns推送了。也可以创建透传消息进行测试了。个推网站只支持发送透传消息。证书测试时是测试的apns消息。

遇到的问题1:在苹果的远程通知注册成功委托函数didRegisterForRemoteNotificationsWithDeviceToken中获得token成功,然后修改别名: [GeTuiSdk bindAlias:@“个23推11” andSequenceNum:@“s1eq-1”]; 那两个参数我无论怎么改都是报30002错误,

我可是按照文档来的的啊!

为何文档上把上面的例子的格式推翻了呢?为何不举一个标准的别名例子呢?

- (void)GeTuiSdkDidRegisterClient:(NSString *)clientId
{
    FLDDLogDebug(@"函数 clientId:%@", clientId);
    if(!isEmptyString(clientId) && (!self.clientId || ![clientId isEqualToString:self.clientId]))
    {
        NSLog(@"clientId:%@", clientId);
        //        [GeTuiSdk bindAlias:@"y790715966c94d93e84182fbfce36182123456GS" andSequenceNum:@"1sssas2223446"];
        if(self.payloadFlag && !self.noFirstGetClientIdFlag)
        {
            [GeTuiSdk resetBadge]; //重置角标计数
            [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; // APP 清空角标
        }
        self.noFirstGetClientIdFlag = YES;
        self.payloadFlag = NO;
        
//        [GeTuiSdk resetBadge]; //重置角标计数
//        [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; // APP 清空角标
        
//        [[NSNotificationCenter defaultCenter] postNotificationName:@"submitNotification" object:nil userInfo:@{@"clientId":clientId}];
        [self submitWithUserInfo:@{@"clientId":clientId}];
    }
}

通过这个问题,大家知道了吧!cid回调以后,调用设置标签,别名绑定等接口,清除角标。

遇到的问题2:我在个推网站测试时,证书测试正常,但是测试推送消息时,有很多消息我手机收不到。咨询了客服还是找不到,在下班时突然收到一条推送消息。第二天我继续定位。

5.2 苹果 APNs 静默推送
如果需要使用 静默推送(Remote Notifications)功能,请在推送时指定content-available:1参数。

根据文档apns推送content-available为1。

可以看到个推测试网站说iOS只支持透传消息,当然apns也可以测试,那就是证书测试。当设置content-available为1时,下面的title和body输入框变灰色,没有办法输入,但是若先输入内容再选择为1,那里面也有内容。结果我推送这样的消息,手机是收不到消息的。文档上不是说content-available:1是静默推送(apns)吗?文档和测试网站的配置为何有看似相互矛盾的说明和配置呢?我本着宁可错过不可放过的原则,把content-available选择为0,title和body输入框可以输入了,输入内容,应用在后台时终于收到消息了。但是在前台时仍旧收不到消息。然后就检查个推的接口函数,发现是拷贝的以前个推的老函数,以前在做曹操专车时用的也个推,那时候个推比较老,现在新的个推函数很多都变了。是应用在前台时,被透传函数收到了,只是我没有把透传函数名(GeTuiSdkDidReceivePayloadData)写正确,写的是老函数名(GexinSdkDidReceivePayload)。后来咨询个推客服,他们说个推现在服务端只支持iOS的透传模版。应用在前台收透传消息,在后台收apns消息。我被这个content-available要搞的脑神经分裂了,切记切记。

遇到的问题3:然后和服务器联调发现,在个推网站测试能收到开发环境证书的应用推送来的消息。收不到服务发送来推送消息,安卓手机测试时可以收到消息。于是就各种的着急。服务开发给的答复,他是按照个推文档第一个点击通知打开应用模板推送的消息。我让他试一试其它推送模版。测试发现只有第4个透传消息模版推送的消息,app能收到。

后来咨询个推客服,他说服务器向iOS普通推送时只能使用第4个透传消息模版推送的消息。

遇到的问题4:我们的领导要求像其它的应用,无论应用在前台和后台都要能收到系统状态栏的消息。

我的思路是:应用在后台消息自动出现在系统消息栏,在前台收到消息自己显示本地通知消息。具体的说是,应用在后台或应用没有启动,很简单走的是apns通道,这个你可以不需要管,若你的手机按装你的应用的情况下,消息会出现在系统消息栏。在你应用在前台时,走的是透传消息,被GeTuiSdkDidReceivePayloadData函数(早期的个推函数是GexinSdkDidReceivePayload。注意早期个推和新的个推函数的变更。这个我走了半天弯路。)接收,不走apns消息通道,走的是个推服务器和app直接的长连接通道,然后自己弹出本地日历通知。

                UILocalNotification *notification = [[UILocalNotification alloc] init];
                if (notification != nil) {
                    // 设置推送时间
                    notification.fireDate = [NSDate date];
                    // 设置时区
                    notification.timeZone = [NSTimeZone defaultTimeZone];
                    // 设置重复间隔
                    notification.repeatInterval = 0;
                    // 推送声音
                    notification.soundName = UILocalNotificationDefaultSoundName;
                    if (@available(iOS 8.2, *)) {
                        notification.alertTitle = title;
                    } else {
                        // Fallback on earlier versions
                    }
                    // 推送内容
                    notification.alertBody = body;
                    notification.userInfo = jsonObject;
                    notification.category = payloadMsg;
                    //显示在icon上的红色圈中的数子
                    notification.applicationIconBadgeNumber =0;
                    //添加推送到UIApplication
                    UIApplication *app = [UIApplication sharedApplication];
                    [app scheduleLocalNotification:notification];
                }

注意:这个通知本质是一个日历事件,可以部分模拟系统通知,但不能完全代替。iOS8.2之前只能设置消息内容,不能设置消息标题。iOS8.2及以后只能设置消息内容和标题,不能设置payload等自定义键值对。透传函数收到的是推送的消息内容,title和body都看不到。详细见个推发送消息各字段在系统状态栏显示的信息,点击消息图标激活应用传递的信息,应用在线收到的透传消息信息。若消息内容含有title和body键值 json串,就可以解析出来弹出本地通知。

遇到的问题5:应用在前台,由于弹出的是本地通知(日历事件),而它最多只能携带标题和内容,无法携带自定义payload字段。所以无法实现拉掉应用,点击消息图标打开app并进入对应消息页面。

咨询个推客服,他说应用在线时只能走透传弹出本地通知。看来既然用个推就要遵循个推的规则。这个是框架问题,不是技术问题。安卓手机,无论应用是否在线都是把消息显示在系统消息栏中,点击消息,若应用不在线直接打开应用进入对应页面。看来苹果手机和安卓手机的应用也很难做到这方面的统一,不知道除个推外的第三方推送的逻辑是否和它一样。

遇到的问题6:我想在个推网站发送推送消息时,只给我的测试手机。个推提送了推送给特定用户。


我在文档文件里找cid列表格式,找了很就久,竟然没有找到。于是去骚扰度娘,她也不给里,找了半天也没有找到,接着找,终于在一个度娘的犄角旮旯里找到了。说每个cid占一行,多个cid之间用换行符号分开,文本文件。马上用文本编译器建立一个只含一个cid(从xcode的打印控制台日志里拷贝出来的)的列表,软后测试,还是推送不到。怀疑是文件格式不正确,转换成纯文本文件,再试还是不行。把cid后面加个换行符然后再测试,终于成功了。 个推cid列表文件。

遇到的问题7:iOS10及以后的推送操作新特性集成。iOS10及以后需要按照个推文档,增加一个NotificationService,Bundle Identifier要是主Target的Bundle Identifie(com.yixiang.agent)开头(com.yixiang.agent.NotificationService)。如怕有问题就按照我下图配置吧!然后按照个推文档增加对应的代码就可以。测试确实哪些新功能确实生效了。

遇到的问题8:在测试环境测试完毕,一切OK。再次转战发布环境进行测试。结果落下一地的鸡毛。首先发布环境p12证书测试一直失败。

遇到问题按照我怀疑一切的个性。我怀疑:难道通过配置测试证书的app和正式证书的app,他们的token不同吗?经过打印token,发现在使用正式证书时,产生token确实不同,个推产生的cid也不同。我输入token正确然后测试生产p12证书还是不正确。看来问题和token有关,但是还有其他问题。我怀疑时p12证书有问题,我记得,我配置测试环境p12证书时是测试过了,配置生产环境证书时,我忘记是否测试过。

登录研发者账号,发现我的生产证书处于invalid状态(当时没有截图)。why?我的证书生成还不到半月,怎么是invalid状态呢?仔细想一想,我最近只进行了一次通过xcode同步一个ios8的手机配置(定位app打开外部链接在iOS9前后走的那个- (BOOL)application函数问题,以便于实现模块化开发。苹果的自动把设备加入研发测试手机列表),可能和那次操作有关。重新生成证书,再次测试生产环境的p12证书。这次终于apns证书测试通过了。

遇到的问题9:按照个推文档,生产环境开发和测试环境需要分开测试。那么生产环境需要生用生产证书,当配置生产证书时,无法把应用直接安装到手机上,需要把ipa文件上传苹果商店,授权测试用户,用苹果的test flight软件下载测试,那样太麻烦了。那是否有更直接的方案呢?带有生产环境的证书性质,又可以安装到测试机上面呢?苹果还真有这种证书:

虽然你要生成一个hot证书,并且不能联调,只能安装测试,和正式的生产证书有所不同,但是他们的功能已经很接近了,可以部分代替生产证书进行测试。注意:hot证书,生产证书,测试证书他们对应的token,cid都不相同。

遇到的问题9:生产环境含有NotificationService的证书配置。推送测试环境,只需要把两个tartgets->General->Signing勾选 Automatically manage signing,电脑导入开发环境证书就可以测试了。然而生产环境要配置正确的证书,可没有这种傻瓜模式。只能去掉勾选Automatically manage signing。在Build Settings->Signing配置正确的证书。

如果你对Targets的NotificationService不配置证书或者配置和主Target的ArtEnjoymentWeChatAuction一样的发布证书会编译不过。经过咨询个推技术客户,他们说要对两个Bundle Identifie创建两个不同的证书。我经过紧张的证书制作。终于创建一个生产证书,一个hot证书在加上原来的测试证书,一个app共三个证书。

按照上面配置好证书终于不报错误了。可以愉快的玩耍了。

遇到的问题10:既然主Target和NotificationService都有自己的生产证书,那么我们p12证书是使用的那个呢?

咨询个推客服,他说用com.yixiang.agent.NotificationService产生的证书。

结果报一个新的错误:连接异常。

买嘎,为什么受伤的总是我。反正这个问题是非正既反的问题,反着来试一试,ok,hot证书推送消息通过。呼唤服务器总部推送生产环境消息,测试pass。

综合问题9和问题10,发现由com.yixiang.agent.NotificationService产生的证书表面看只是帮助编译通过,没有其它大用,真正个推应用配置的生产证书还是主Target的com.yixiang.agent产生的p12证书。

一个支持个推推送的应用在研发者中心需要创建的证书有5个必须的证书和两个非必须的hot证书 :生产证书,测试环境证书,NotificationService证书 产生生产环境(包括 hot 证书)的p12证书的证书,产生测试环境的p12证书的证书,hot证书(非必须),NotificationService的hot证书(非必须)。

遇到的问题11:上传苹果商店时报ERROR ITMS-90715错误。

ERROR ITMS-90715: "Minimum OS too low. The Payload/ArtEnjoymentWeChatAuction.app/PlugIns/NotificationService.appex extension requires a version of iOS higher than the value specified for the MinimumOSVersion key in Info.plist."
WARNING ITMS-90473: "CFBundleVersion Mismatch. The CFBundleVersion value '1.0.0' of extension 'ArtEnjoymentWeChatAuction.app/PlugIns/NotificationService.appex' does not match the CFBundleVersion value '1.0.3' of its containing iOS application 'ArtEnjoymentWeChatAuction.app'."
WARNING ITMS-90473: "CFBundleShortVersionString Mismatch. The CFBundleShortVersionString value '1.0.0' of extension 'ArtEnjoymentWeChatAuction.app/PlugIns/NotificationService.appex' does not match the CFBundleShortVersionString value '1.0.3' of its containing iOS application 'ArtEnjoymentWeChatAuction.app'."

修改target的NotificationService版本号和target的ArtEnjoymentWeChatAuction版本号一致,然后打包上传就可以。报错如下:

ERROR ITMS-90715: "Minimum OS too low. The Payload/ArtEnjoymentWeChatAuction.app/PlugIns/NotificationService.appex extension requires a version of iOS higher than the value specified for the MinimumOSVersion key in Info.plist."

原来是Deployment Target版本支持的是8.0引起的。

这个是个推客服的回答。我删除Targets下的NotificationService重新打开工程打包上串苹果商店就可以了。

注意:iOS8是iPhone 6安装的初始版本,现在市场上还有大量的iPhone 6手机,有的用户从来没有升级过苹果系统。升级操作系统是有风险的,我的iPhone 5s手机升级操作系统就遇到过升级成砖头了。我估计是手机空间不足引起的,也可能是一直提示验证系统我等不及了(5分钟左右)把手机重启了。幸亏我是搞app开发的,通过iTunes还原了系统,结果我所有的数据和应用都没有了,安装了两天app。

遇到的问题12:当有应用在后台或没有启动,收到apns消息后,应用角标数字增加。无论是点击应用图标启动应用还是点击系统消息启动应用都不能清除角标。我们的应用还没有做消息列表页面,那如何清除角标呢?只有获取到 clientId(cid)后才能判断设置重置角标。我的做法是,应用启动是根据launchOptions是否为空来判断是否是点击系统消息启动应用,若非空(我们的apns消息的自定义键payload对应的值都可以解析出redirectUrl)就在获取到 clientId(cid)后重置角标。launchOptions解析见添加链接描述《点击app系统消息打开app并进入指定页面》。

- (void)GeTuiSdkDidRegisterClient:(NSString *)clientId
{
    FLDDLogDebug(@"函数 clientId:%@", clientId);
    if(!isEmptyString(clientId) && (!self.clientId || ![clientId isEqualToString:self.clientId]))
    {
        NSLog(@"clientId:%@", clientId);
        if(self.payloadFlag && !self.noFirstGetClientIdFlag)
        {
            [GeTuiSdk resetBadge]; //重置角标计数
            [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; // APP 清空角标
        }
        self.noFirstGetClientIdFlag = YES;
        self.payloadFlag = NO;
        [self submitWithUserInfo:@{@"clientId":clientId}];
    }
}

注意:由于应用每次从后台切换到前台都触发GeTuiSdkDidRegisterClient函数,所以要只第一获取到 clientId(cid)后才重置角标。

来点干货,个推主代码如下,由于我们的app采用的是组件化,推送的代码在推送组件里处理,为了便于大家理解我把他移植到AppDelegate里以便于大家理解:

AppDelegate.h

#import <Foundation/Foundation.h>
#import <GTSDK/GeTuiSdk.h>     // GetuiSdk头文件应用
// iOS10 及以上需导入 UserNotifications.framework
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
#import <UserNotifications/UserNotifications.h>
#endif

@interface AppDelegate : UIResponder<UIApplicationDelegate, GeTuiSdkDelegate, UNUserNotificationCenterDelegate>

@end

AppDelegate.m

#import "AppDelegate.h"
@interface AppDelegate ()<WXApiDelegate>

@property (nonatomic, assign) BOOL isLaunch;
@property (nonatomic, strong) NSString *clientId; //上传个推clientId,每次应用启动和从后台切换到前台应用都重新获取到clientId
@property (nonatomic, strong) NSString *deviceToken; //上传个推deviceToken
@property (nonatomic, assign) BOOL existCookieFlag; //存在cookie标志
@property (nonatomic, assign) BOOL noFirstGetClientIdFlag; //每次应用从前台切换到后台就重新获取到ClientId,处理点击系统中的消息图标启动应用第一次获取到ClientId时重置角标
@property (nonatomic, assign) BOOL payloadFlag; //标记点击系统中的消息图标启动应用,为YES时,第一次获取到ClientId时重置角标
@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    FLDDLogVerbose(@"didFinishLaunchingWithOptions");
    // [EXT] 重新上线
    [GeTuiSdk startSdkWithAppId:kGeXinAppId appKey:kGeXinAppKey appSecret:kGeXinAppSecret delegate:self];
    // 注册 APNs
    [self registerRemoteNotification];
    // [2-EXT]: 获取启动时收到的APN
    NSDictionary* message = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    
    FLDDLogVerbose(@"didFinishLaunchingWithOptions  message:%@,[message className]:%@", message, [message className]);
    if (message) {
//        [AWSingleObject sharedInstance].redirectUrl = @"https://m.1-joy.com/market/cat/list.htm";
        NSString *payload = [message objectForKey:@"payload"];
        FLDDLogVerbose(@"payload:%@,[payload className]:%@", payload, [payload className]);
        if(payload)
        {
            self.payloadFlag = YES;
            NSData* jsondata = [payload dataUsingEncoding:NSUTF8StringEncoding];
            FLDDLogVerbose(@"jsondata:%@,[jsondata className]:%@", jsondata, [jsondata className]);
            NSError *error = nil;
            id jsonObject = [NSJSONSerialization JSONObjectWithData:jsondata options:NSJSONReadingAllowFragments error:&error];
            
            FLDDLogVerbose(@"jsonObject:%@,[jsonObject className]:%@,[jsonObject isKindOfClass:[NSDictionary class]]:%d, error:%@", jsonObject ,[jsonObject className], [jsonObject isKindOfClass:[NSDictionary class]], error);
            if(!error && jsonObject && [jsonObject isKindOfClass:[NSDictionary class]])
            {
                NSString *redirectUrl = [jsonObject safeObjectForKey:@"redirectUrl"];
                FLDDLogVerbose(@"redirectUrl:%@,[redirectUrl className]:%@", redirectUrl, [redirectUrl className]);
                if(redirectUrl)
                {
                    [AWSingleObject sharedInstance].redirectUrl = redirectUrl;
//                    [[NSNotificationCenter defaultCenter] postNotificationName:@"redirectLoginNotification" object:nil userInfo:@{@"redirectUrl":@"http://getui.com\\"}];
                }
            }
        }
//        NSString *record = [NSString stringWithFormat:@"[APN]%@, %@", [NSDate date], payload];
        
        //如何跳转页面自己添加代码
        //
        //        self.window.rootViewController = self.viewController;
    }
    
    self.isLaunch = YES;
    return YES;
}

/** 注册 APNs */
- (void)registerRemoteNotification {
    /*
     警告:Xcode8 需要手动开启"TARGETS -> Capabilities -> Push Notifications"
     */
    
    /*
     警告:该方法需要开发者自定义,以下代码根据 APP 支持的 iOS 系统不同,代码可以对应修改。
     以下为演示代码,注意根据实际需要修改,注意测试支持的 iOS 系统都能获取到 DeviceToken
     */
    if (@available(iOS 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];
    }
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    NSLog(@"\nuserInfo:%@\n\n", userInfo);
    // 将收到的APNs信息传给个推统计
    [GeTuiSdk handleRemoteNotification:userInfo];
    completionHandler(UIBackgroundFetchResultNewData);
}

/** 远程通知注册成功委托 */
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    NSString *token = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    token = [token stringByReplacingOccurrencesOfString:@" " withString:@""];
    FLDDLogDebug(@"\n>>>[DeviceToken Success]:%@\n\n kGeXinAppId:%@", token, kGeXinAppId);
    if(isEmptyString(token))
    {
        return;
    }
//    [[NSNotificationCenter defaultCenter] postNotificationName:@"submitNotification" object:nil userInfo:@{@"deviceToken":token}];
    [self submitWithUserInfo:@{@"deviceToken":token}];
    // 向个推服务器注册deviceToken
    [GeTuiSdk registerDeviceToken:token];
    NSString *clientId = [GeTuiSdk clientId];
    FLDDLogDebug(@"函数 clientId:%@", clientId);
    if(!isEmptyString(clientId))
    {
        NSLog(@"clientId:%@", clientId);
    }
    
}

#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)){
    
    NSLog(@"willPresentNotification:%@", notification.request.content.userInfo);
    
    // 根据APP需要,判断是否要提示用户Badge、Sound、Alert
    if (@available(iOS 10.0, *)) {
        completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
    } else {
        // Fallback on earlier versions
    }
}

//  iOS 10: 点击通知进入App时触发,在该方法内统计有效用户点击数
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler  API_AVAILABLE(ios(10.0)){
    
    NSLog(@"didReceiveNotification:%@", response.notification.request.content.userInfo);
    
    [GeTuiSdk resetBadge]; //重置角标计数
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; // APP 清空角标

    // [ GTSdk ]:将收到的APNs信息传给个推统计
    [GeTuiSdk handleRemoteNotification:response.notification.request.content.userInfo];
    
    completionHandler();
}

#endif


#pragma mark - GexinSdkDelegate
- (void)GexinSdkDidOccurError:(NSError *)error
{
    // [EXT]:个推错误报告,集成步骤发生的任何错误都在这里通知,如果集成后,无法正常收到消息,查看这里的通知。
    NSLog(@">>>[GexinSdk error]:%@", [error localizedDescription]);
}

/** SDK收到透传消息回调 */
- (void)GeTuiSdkDidReceivePayloadData:(NSData *)payloadData andTaskId:(NSString *)taskId andMsgId:(NSString *)msgId andOffLine:(BOOL)offLine fromGtAppId:(NSString *)appId {
    //收到个推消息
    NSString *payloadMsg = nil;
    if ((payloadData) && ([payloadData isKindOfClass:[NSData class]])) {
        payloadMsg = [[NSString alloc] initWithBytes:payloadData.bytes length:payloadData.length encoding:NSUTF8StringEncoding];
        
//        NSData* jsondata = [payload dataUsingEncoding:NSUTF8StringEncoding];
        FLDDLogVerbose(@"payloadData:%@,[payloadData className]:%@", payloadData, [payloadData className]);
        NSError *error = nil;
        id jsonObject = [NSJSONSerialization JSONObjectWithData:payloadData options:NSJSONReadingAllowFragments error:&error];
        
        FLDDLogVerbose(@"jsonObject:%@,[jsonObject className]:%@,[jsonObject isKindOfClass:[NSDictionary class]]:%d, error:%@", jsonObject ,[jsonObject className], [jsonObject isKindOfClass:[NSDictionary class]], error);
        if(!error && jsonObject && [jsonObject isKindOfClass:[NSDictionary class]])
        {
            NSString *redirectUrl = [jsonObject safeObjectForKey:@"redirectUrl"];
            NSString *title = [jsonObject safeObjectForKey:@"title"];
            NSString *body = [jsonObject safeObjectForKey:@"body"];
            FLDDLogVerbose(@"redirectUrl:%@,[redirectUrl className]:%@", redirectUrl, [redirectUrl className]);
            if((title && [title isKindOfClass:[NSString class]]) || (body && [body isKindOfClass:[NSString class]]))
            {
                redirectUrl = isEmptyString(redirectUrl) ? @"" : redirectUrl;
                title = isEmptyString(title) ? @"" : title;
                body = isEmptyString(body) ? @"" : body;
                UILocalNotification *notification = [[UILocalNotification alloc] init];
                if (notification != nil) {
                    // 设置推送时间
                    notification.fireDate = [NSDate date];
                    // 设置时区
                    notification.timeZone = [NSTimeZone defaultTimeZone];
                    // 设置重复间隔
                    notification.repeatInterval = 0;
                    // 推送声音
                    notification.soundName = UILocalNotificationDefaultSoundName;
                    if (@available(iOS 8.2, *)) {
                        notification.alertTitle = title;
                    } else {
                        // Fallback on earlier versions
                    }
                    // 推送内容
                    notification.alertBody = body;
                    notification.userInfo = jsonObject;
                    notification.category = payloadMsg;
                    //显示在icon上的红色圈中的数子
                    notification.applicationIconBadgeNumber =0;
                    //添加推送到UIApplication
                    UIApplication *app = [UIApplication sharedApplication];
                    [app scheduleLocalNotification:notification];
                }
            }
        }
    }
    
    NSString *msg = [NSString stringWithFormat:@"taskId=%@,messageId:%@,payloadMsg:%@%@",taskId,msgId, payloadMsg,offLine ? @"<离线消息>" : @""];
    NSLog(@"\n>>>[GexinSdk ReceivePayload]:%@\n\n", msg);
}

/**
 *  SDK登入成功返回clientId
 *
 *  @param clientId 标识用户的clientId
 *  说明:启动GeTuiSdk后,SDK会自动向个推服务器注册SDK,当成功注册时,SDK通知应用注册成功。
 *  注意: 注册成功仅表示推送通道建立,如果appid/appkey/appSecret等验证不通过,依然无法接收到推送消息,请确保验证信息正确。
 */
- (void)GeTuiSdkDidRegisterClient:(NSString *)clientId
{
    FLDDLogDebug(@"函数 clientId:%@", clientId);
    if(!isEmptyString(clientId) && (!self.clientId || ![clientId isEqualToString:self.clientId]))
    {
        NSLog(@"clientId:%@", clientId);
        //        [GeTuiSdk bindAlias:@"y790715966c94d93e84182fbfce36182123456GS" andSequenceNum:@"1sssas2223446"];
        if(self.payloadFlag && !self.noFirstGetClientIdFlag)
        {
            [GeTuiSdk resetBadge]; //重置角标计数
            [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; // APP 清空角标
        }
        self.noFirstGetClientIdFlag = YES;
        self.payloadFlag = NO;
        
//        [GeTuiSdk resetBadge]; //重置角标计数
//        [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; // APP 清空角标
        
//        [[NSNotificationCenter defaultCenter] postNotificationName:@"submitNotification" object:nil userInfo:@{@"clientId":clientId}];
        [self submitWithUserInfo:@{@"clientId":clientId}];
    }
}

/**
 *  SDK运行状态通知
 *
 *  @param aStatus 返回SDK运行状态
 */
- (void)GeTuiSDkDidNotifySdkState:(SdkStatus)aStatus;
{
    FLDDLogDebug(@"aStatus:%d", aStatus);
}
/**
 *  SDK设置关闭推送模式回调
 *
 *  @param isModeOff 关闭模式,YES.服务器关闭推送功能 NO.服务器开启推送功能
 *  @param error     错误回调,返回设置时的错误信息
 */
- (void)GeTuiSdkDidSetPushMode:(BOOL)isModeOff error:(NSError *)error;
{
    FLDDLogDebug(@"isModeOff:%d, error:%@", isModeOff, error);
}
/**
 *  SDK绑定、解绑回调
 *
 *  @param action       回调动作类型 kGtResponseBindType 或 kGtResponseUnBindType
 *  @param isSuccess    成功返回 YES, 失败返回 NO
 *  @param aSn          返回请求的序列码
 *  @param aError       成功返回nil, 错误返回相应error信息
 */
- (void)GeTuiSdkDidAliasAction:(NSString *)action result:(BOOL)isSuccess sequenceNum:(NSString *)aSn error:(NSError *)aError;
{
    FLDDLogDebug(@"action:%@, isSuccess:%d, aSn:%@, aError:%@", action, isSuccess, aSn, aError);
}

-(void)submit:(NSNotification *)notification
{
    NSDictionary* userInfo = [notification userInfo];
    [self submitWithUserInfo:userInfo];
}

-(void)submitWithUserInfo:(NSDictionary *)userInfo
{
    NSLog(@"userInfo:%@", userInfo);
    NSString *existCookieFlag = [userInfo objectForKey:@"existCookieFlag"];
    if(!isEmptyString(existCookieFlag))
    {
        if([existCookieFlag isEqualToString:@"YES"])
        {
            self.existCookieFlag = YES;
        }
        else
        {
            self.existCookieFlag = NO;
        }
    }
    NSString *deviceToken = [userInfo objectForKey:@"deviceToken"];
    if(!isEmptyString(deviceToken))
    {
        self.deviceToken = deviceToken;
    }
    NSString *clientId = [userInfo objectForKey:@"clientId"];
    if(!isEmptyString(clientId))
    {
        self.clientId = clientId;
    }
    
    if(isEmptyString(self.clientId ) || !(self.existCookieFlag))
    {
        return;
    }
    
    NSString *urlStr = [NSString stringWithFormat:@"%@getui/dd.htm?appId=%@&type=ios",  kBaseURL, kGeXinAppId];
    if(!isEmptyString(self.clientId))
    {
        urlStr = [NSString stringWithFormat:@"%@&clientId=%@", urlStr, self.clientId];
    }
    if(!isEmptyString(self.deviceToken))
    {
        urlStr = [NSString stringWithFormat:@"%@&deviceToke=%@", urlStr, self.deviceToken];
    }
    urlStr = [urlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];//编码
    NSLog(@"urlStr:%@", urlStr);
    NSURL * url = [NSURL URLWithString:urlStr];
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
    NSURLSession * session = [NSURLSession sharedSession];
    NSString *cookie = [self readCurrentCookieWithDomain:urlStr];
    [request addValue:cookie forHTTPHeaderField:@"Cookie"];
    // 发送请求
    NSURLSessionTask * sessionTask = [session dataTaskWithRequest:request
                                                completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                    if (error) {
                                                        return;
                                                    }
                                                    NSString *mmmmmmm = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                                                    NSLog(@"urlStr:%@ error:%@, request.allHTTPHeaderFields:%@", urlStr, error, request.allHTTPHeaderFields);
                                                    NSLog(@"mmmmmmm: %@, response:%@", mmmmmmm, response);
                                                }];
    [sessionTask resume];
}

- (NSString *)readCurrentCookieWithDomain:(NSString *)domainStr{
    NSHTTPCookieStorage*cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    NSMutableString * cookieString = [[NSMutableString alloc]init];
    for (NSHTTPCookie*cookie in [cookieJar cookies]) {
        [cookieString appendFormat:@"%@=%@;",cookie.name,cookie.value];
    }
    
    //删除最后一个“;”
    [cookieString deleteCharactersInRange:NSMakeRange(cookieString.length - 1, 1)];
    return cookieString;
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    self.isLaunch = NO;
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    if (self.isLaunch) {
        return;
    }
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}


@end


目录
相关文章
|
1月前
|
Docker 容器
Star 1.8k! 推荐一款更优雅的微信文章订阅工具,让阅读更便捷!
Star 1.8k! 推荐一款更优雅的微信文章订阅工具,让阅读更便捷!
|
1月前
|
移动开发 小程序 安全
【社区每周】小程序基础2.7.23版本上线;卡证识别功能全面公测(2022年8月第二期)
【社区每周】小程序基础2.7.23版本上线;卡证识别功能全面公测(2022年8月第二期)
40 0
|
1天前
|
开发工具
微信客服系统开发SDK使用教程- 拉取当前微信个人号列表请求(立即)
微信客服系统开发SDK使用教程- 拉取当前微信个人号列表请求(立即)
|
1月前
|
数据可视化 数据挖掘 Serverless
数据推送上线 ,抢先体验,欢迎来用! 支持钉钉!
大多数业务都会有定期推送业务信息至钉钉、飞书、Teams 群的需求,有些信息要推三个群、要推两个群、有些信息要 at 人、有些要当天、有些要当月,不旦要管理多个推送的 Webhook,还要管理推送的内容、监控推送是否生效等等,DataWorks 新推出的数据推送能减轻以上问题,还能助力快速完成推送内容开发,支持规范的上线流程。
76 5
|
6月前
|
存储 网络安全 对象存储
使用Flink实时发现Github最热项目之免费试用开通教程文档
使用Flink实时发现Github最热项目之免费试用开通教程文档
166 1
|
消息中间件 视频直播 数据安全/隐私保护
短视频直播系统,构建礼物模块需注意的问题
短视频直播系统,构建礼物模块需注意的问题
|
缓存 监控 测试技术
​微信视频号下载器免费版下载 (微信视频号视频下载)
​微信视频号下载器免费版下载 (微信视频号视频下载)
​微信视频号下载器免费版下载 (微信视频号视频下载)
|
移动开发 JavaScript 前端开发
ReactNative集成个推消息推送
ReactNative集成个推消息推送
676 0
ReactNative集成个推消息推送
|
搜索推荐 5G 开发者
5G消息应用号推荐|官方种草清单第六期
招聘、交友、找房子、挂号、问诊!覆盖这么多生活场景,真的要好好体验一下! 让我们来看看,本期官方种草清单,又带来哪些优秀的5G消息应用号吧!
5G消息应用号推荐|官方种草清单第六期
|
搜索推荐 5G UED
5G消息应用号推荐|官方种草清单第二期
高考信息一手掌握,乡村服务线上体验,中小企业服务全新方式!5G消息应用号推荐官方种草清单第二期真的很Nice!快来小5一起体验!
5G消息应用号推荐|官方种草清单第二期