今天我们要讲的内容主要是针对 Cordova 环境下添加百度推送时产生的问题及处理

因为项(jia)目(fang)需要,选择的百度推送添加到 app 中,当然,你们就算百度也能找到 Cordova 的百度推送插件,无脑安装就可以……

不过嘛,本着求人不如求己(其实是嫌麻烦不想弄+想踩坑)的原则我要说的是我个人手动添加调试百度推送的过程以及坑

首先,百度云推送中心弄SDK,看官方文档,介些基本操作就不说多了,不会就转行吧

创建测试 demo,添加推送证书

然后就是正常的创建操作,这里要说的是他这个证书,百度家不走寻常路不需要我们的 p12 文件,而是要我们 p12 再生成的 pem 文件,这个着实恶心到我了,而更恶心的是这个东西百度家还很诡异的因为 Mac 系统版本高于 10.12.6 的 openssl 版本是 2.x.x,百度推送不接受高于 0.9.8 的 openssl 生成的 pem。

EXM???

没办法,我只好去openssl历史版本库中去找 openssl 0.9.8zh 版本下载安装

下载 openssl 0.9.8zh -> 解压下载的压缩包 -> cd 你解压后的压缩包目录

执行命令

./Configure darwin64-x86_64-cc --prefix=/usr/local/openssl --shared

这一步可能会遇到没有权限创建openssl文件夹的问题,可以手动到/usr/local/下创建一个openssl文件夹,然后在执行命令(不知道的话就随便开个文件夹 shift+command+g(windows 键盘的 command 就是 win 键) 然后输入 /usr/local/ 回车就到了)

然后

make && make install

openssl将会出现在这个路径下:/usr/local/openssl/bin/openssl,命令行查看下版本:

/usr/local/openssl/bin/openssl version

显示0.9.8zh就成功了

然后 p12 生成 pem

/usr/local/openssl/bin/openssl pkcs12 -in 开发/生产.p12 -out 开发/生产.pem -nodes

保存好,把对应的 pem 文件给百度传上去就可以了(出处

然后是项目添加 BPush,这个用官方 Demo 里的就行,根据你个人的项目需求自行选择 normalversion / idfaversion 二者区别是 如果你用 idfaversion 的话在审核的时候你是要选 广告标识符 为“是”的,另外文件夹里的 test.caf 也不需要(一音乐文件,不懂他用在什么地方,反正我是不需要)

放到项目里后,在 appdelegate 文件中导入

#import "BPush.h"
#ifdef NSFoundationVersionNumber_iOS_9_x_Max
#import <UserNotifications/UserNotifications.h>
#endif

以及文件中涉及推送的那一大串代码(较长,请仔细甄别):

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // iOS10 下需要使用新的 API
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10.0) {
#ifdef NSFoundationVersionNumber_iOS_9_x_Max
        UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];

        [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge)
                              completionHandler:^(BOOL granted, NSError * _Nullable error) {
                                  // Enable or disable features based on authorization.
                                  if (granted) {
                                      [application registerForRemoteNotifications];
                                  }
                              }];
#endif
    }
    else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
        UIUserNotificationType myTypes = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;

        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:myTypes categories:nil];
        [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
    }else {
        UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeAlert|UIRemoteNotificationTypeSound;
        [[UIApplication sharedApplication] registerForRemoteNotificationTypes:myTypes];
    }

    #warning 测试 开发环境 时需要修改BPushMode为BPushModeDevelopment 需要修改Apikey为自己的Apikey

    // 在 App 启动时注册百度云推送服务,需要提供 Apikey

    [BPush registerChannel:launchOptions apiKey:@"UYTrEvcwTxIDaAPgEuijvNMz"pushMode:BPushModeProduction withFirstAction:@"打开" withSecondAction:@"关闭" withCategory:@"test" useBehaviorTextInput:YES isDebug:YES];

    // 禁用地理位置推送 需要再绑定接口前调用。

    [BPush disableLbs];

    // App 是用户点击推送消息启动
    NSDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    if (userInfo) {
        [BPush handleNotification:userInfo];
    }
#if TARGET_IPHONE_SIMULATOR
    Byte dt[32] = {0xc6, 0x1e, 0x5a, 0x13, 0x2d, 0x04, 0x83, 0x82, 0x12, 0x4c, 0x26, 0xcd, 0x0c, 0x16, 0xf6, 0x7c, 0x74, 0x78, 0xb3, 0x5f, 0x6b, 0x37, 0x0a, 0x42, 0x4f, 0xe7, 0x97, 0xdc, 0x9f, 0x3a, 0x54, 0x10};
    [self application:application didRegisterForRemoteNotificationsWithDeviceToken:[NSData dataWithBytes:dt length:32]];
#endif
    //角标清0
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
    /*
     // 测试本地通知
    [self performSelector:@selector(testLocalNotifi) withObject:nil afterDelay:1.0];
    */
    return YES;
}

- (void)testLocalNotifi
{
    NSLog(@"测试本地通知啦!!!");
    NSDate *fireDate = [[NSDate new] dateByAddingTimeInterval:5];
    [BPush localNotification:fireDate alertBody:@"这是本地通知" badge:3 withFirstAction:@"打开" withSecondAction:nil userInfo:nil soundName:nil region:nil regionTriggersOnce:YES category:nil useBehaviorTextInput:YES];
}

// 此方法是 用户点击了通知,应用在前台 或者开启后台并且应用在后台 时调起
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
     completionHandler(UIBackgroundFetchResultNewData);
    // 打印到日志 textView 中
    NSLog(@"********** iOS7.0之后 background **********");
    // 应用在前台,不跳转页面,让用户选择。
    if (application.applicationState == UIApplicationStateActive) {
        NSLog(@"acitve ");
//        UIAlertView *alertView =[[UIAlertView alloc]initWithTitle:@"收到一条消息" message:userInfo[@"aps"][@"alert"] delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
//        [alertView show];
    }
    //杀死状态下,直接跳转到跳转页面。
    if (application.applicationState == UIApplicationStateInactive && !isBackGroundActivateApplication)
    {
        SkipViewController *skipCtr = [[SkipViewController alloc]init];
        // 根视图是nav 用push 方式跳转
        [_tabBarCtr.selectedViewController pushViewController:skipCtr animated:YES];
        NSLog(@"applacation is unactive ===== %@",userInfo);
        /*
        // 根视图是普通的viewctr 用present跳转
        [_tabBarCtr.selectedViewController presentViewController:skipCtr animated:YES completion:nil]; */
    }
    // 应用在后台。当后台设置aps字段里的 content-available 值为 1 并开启远程通知激活应用的选项
    if (application.applicationState == UIApplicationStateBackground) {
        NSLog(@"background is Activated Application ");
        // 此处可以选择激活应用提前下载邮件图片等内容。
        isBackGroundActivateApplication = YES;
//        UIAlertView *alertView =[[UIAlertView alloc]initWithTitle:@"收到一条消息" message:userInfo[@"aps"][@"alert"] delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
//        [alertView show];
    }
    [self.viewController addLogString:[NSString stringWithFormat:@"Received Remote Notification :\n%@",userInfo]];

    NSLog(@"%@",userInfo);
}

// 在 iOS8 系统中,还需要添加这个方法。通过新的 API 注册推送服务
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{

    [application registerForRemoteNotifications];


}

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    NSLog(@"test:%@",deviceToken);
    [BPush registerDeviceToken:deviceToken];
    [BPush bindChannelWithCompleteHandler:^(id result, NSError *error) {
        [self.viewController addLogString:[NSString stringWithFormat:@"Method: %@\n%@",BPushRequestMethodBind,result]];
        // 需要在绑定成功后进行 settag listtag deletetag unbind 操作否则会失败

        // 网络错误
        if (error) {
            return ;
        }
        if (result) {
            // 确认绑定成功
            if ([result[@"error_code"]intValue]!=0) {
                return;
            }
            // 获取channel_id
            NSString *myChannel_id = [BPush getChannelId];
            NSLog(@"==%@",myChannel_id);

            [BPush listTagsWithCompleteHandler:^(id result, NSError *error) {
                if (result) {
                    NSLog(@"result ============== %@",result);
                }
            }];
            [BPush setTag:@"Mytag" withCompleteHandler:^(id result, NSError *error) {
                if (result) {
                    NSLog(@"设置tag成功");
                }
            }];
        }
    }];

    // 打印到日志 textView 中
    [self.viewController addLogString:[NSString stringWithFormat:@"Register use deviceToken : %@",deviceToken]];


}

// 当 DeviceToken 获取失败时,系统会回调此方法
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
    NSLog(@"DeviceToken 获取失败,原因:%@",error);
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    // App 收到推送的通知
    [BPush handleNotification:userInfo];
    NSLog(@"********** ios7.0之前 **********");
    // 应用在前台 或者后台开启状态下,不跳转页面,让用户选择。
    if (application.applicationState == UIApplicationStateActive || application.applicationState == UIApplicationStateBackground) {
        NSLog(@"acitve or background");
        UIAlertView *alertView =[[UIAlertView alloc]initWithTitle:@"收到一条消息" message:userInfo[@"aps"][@"alert"] delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
        [alertView show];
    }
    else//杀死状态下,直接跳转到跳转页面。
    {
        SkipViewController *skipCtr = [[SkipViewController alloc]init];
        [_tabBarCtr.selectedViewController pushViewController:skipCtr animated:YES];
    }

    [self.viewController addLogString:[NSString stringWithFormat:@"Received Remote Notification :\n%@",userInfo]];

    NSLog(@"%@",userInfo);
}

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
    NSLog(@"接收本地通知啦!!!");
    [BPush showLocalNotificationAtFront:notification identifierKey:nil];
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 1) {
        SkipViewController *skipCtr = [[SkipViewController alloc]init];
        // 根视图是nav 用push 方式跳转
        [_tabBarCtr.selectedViewController pushViewController:skipCtr animated:YES];
        /*
         // 根视图是普通的viewctr 用present跳转
         [_tabBarCtr.selectedViewController presentViewController:skipCtr animated:YES completion:nil]; */
    }
}

根据你个人需要自行修改增减代码

对了,别忘了填上你的 apikey

然后关于项目中推送配置 Background Modes、Push Notifications 开关什么的别忘记开

运行,获取到Channel_id,OK!

然后我发现,我成功运行是因为我是直接连手机运行的,非常成功,但是一旦我打包分发给别人测试的时候就报错

Code=3000 “未找到应用程序的“aps-environment”的授权字符串” WTF?!!!

然后经百度谷歌排查发现,推送是要有一个 XXX.entitlements 的文件的,而我没有╮(╯▽╰)╭

然后我又看了下 Build Srttings 里的 Code Signing Entitlements

.plist??? EXM??? Σ(・□・;) 那就要把它换成 .entitlements 文件才行啊!

重新编译、打包、运行测试,成功!

2018-11-13续:

百度推送成功率第及时性差,已经更换成极光推送了╮(╯▽╰)╭……

(后续 BUG 持续开发结束)