Cocos2d-x iOS集成push
2014.10.24 by cocos
教程

前言

使用推送消息,可提醒用户,召回玩家。延长游戏的寿命和收益!下面我们就一起来学习,Cocos2d-x iOS集成push。

收到push消息: imgimg

本文主要内容:

  • iOS Push基本理念
  • 创建工程
  • ios push相关配置
  • push集成
  • push 测试和使用
  • 总结
  • 相关资料

iOS Push基本理念

我们先来了解,iOS push实现的大概原理

img

  • 从上图我们可以看到。
    1. 首先是应用程序注册消息推送。
    2. IOS跟APNS Server要deviceToken。应用程序接受deviceToken。
    3. 应用程序将deviceToken发送给PUSH服务端程序。
    4. 服务端程序向APNS服务发送消息。
    5. APNS服务将消息发送给iPhone应用程序。

无论是iPhone客户端跟APNS,还是Provider和APNS都需要通过证书进行连接的

创建工程

工程基于 Cocos2d-x-3.0rc2 版本, 针对ios平台.

Cocos2d-x-3.0rc2 下载地址 如果已经有Cocos2d-x 引擎可以跳过下载。解压文件进入该目录 创建工程

    $ cd Cocos2d-x
    $ ./setup.py
    $ source FILE_TO_SAVE_SYSTEM_VARIABLE
    $ cocos new pushDemo -p com.your_company.pushDemo -l cpp -d /home
    $ cd /home/pushDemo

push相关配置

  • 登陆 iOS Dev Center 选择进入iOS Provisioning Portal。创建应用程序ID img
  • 在 iOS Provisioning Portal中,点击App IDs进入App ID列表。 img
  • 创建 App ID,如果 ID 已经存在可以直接跳过此步骤 img
  • 为 App 开启 Push Notification 功能。如果是已经创建的 App ID 也可以通过设置开启 Push Notification 功能。 img
  • 根据实际情况完善 App ID 信息并提交,注意此处需要指定具体的 Bundle ID 不要使用通配符。 img
  • push cer创建 develop 和 production 如果你之前没有创建过 Push 证书或者是要重新创建一个新的,请在证书列表下面新建。 img 新建证书需要注意选择证书种类(开发证书用于开发和调试使用,生产证书用于 App Store 发布) img
  • 点击 Continue 后选择证书对应的应用ID,然后继续会出现“About Creating a Certificate Signing Request (CSR)”。 img
  • 根据它的说明创建打开KeychainAccess 创建 Certificate Signing Request。 img
  • 填写“User Email Address”和“Common Name” 后选择 Saved to disk 进行保存 。 img 继续返回Apple developer 网站点击 Continue ,上传刚刚生成的 .certSigningRequest 文件生成 APNs Push Certificate。 下载并双击打开证书,证书打开时会启动“钥匙串访问”工具。 在“钥匙串访问”中你的证书会显示在“我的证书”中,注意选择“My Certificates” 和"login" img
  • 导出 .p12 证书文件 在“钥匙串访问”中,选择刚刚加进来的证书,选择右键菜单中的“导出“...””。 注意要选“login”和“My Certificates” 导出证书时要选中证书文件,不要展开private key。 img 将文件保存为Personal Information Exchange (.p12)格式。 保存p12文件时,可以为其设置密码,也可以让密码为空。
  • app Provisioning Profile 创建,选取app Id, Certificates,Devices. 分develop 和 production img

push集成

  • 新建pushHelper C++类,定义监听push回调相关的接口。
class  pushHelper
{    
public:
    /** returns a shared instance of the pushHelper
     *  @js getInstance
     */
    static pushHelper* sharedPushHelper(void);

    /**
     @brief  The function be called when the application launching receive remote notification
     @param  notificationJson the pointer of the notification json string
     */
    bool applicationDidFinishLaunchingWithNotification(const char* notificationJson);

    /**
     @brief  The function be called when the application register remote notification success
     @param  deviceToken the pointer of the notification token string
     */
    void applicationDidRegisterForRemoteNotificationsWithDeviceToken(const char *deviceToken);

    /**
     @brief  The function be called when the application register remote notification failed
     @param  error the pointer of the register remote notification failed string
     */
    void applicationdidFailToRegisterForRemoteNotificationsWithError(const char *error);

    /**
     @brief  The function be called when the application running receive remote notification
     @param  notificationJson the pointer of the notification json string
     */
    void applicationDidReceiveRemoteNotification(const char* notificationJson);

};
  • 在AppController.mm加入 ios remoteNotification 相关实现

1.在application: didFinishLaunchingWithOptions:注册push的种类,并处理启动时收到push

    //======================push========================

    [application registerForRemoteNotificationTypes:
     UIRemoteNotificationTypeAlert
     | UIRemoteNotificationTypeBadge
     | UIRemoteNotificationTypeSound];

    [application setApplicationIconBadgeNumber:0];

    //启动时收到push delay 5s派发
    NSDictionary * userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    if(userInfo) {
        [application setApplicationIconBadgeNumber:0];
        NSLog(@"LaunchOptionsRemoteNotification:%@",[userInfo description]);

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            pushHelper::sharedPushHelper()->applicationDidFinishLaunchingWithNotification([[userInfo description] cStringUsingEncoding:NSUTF8StringEncoding]);
        });
    }

    //======================push========================

2.在application: didRegisterForRemoteNotificationsWithDeviceToken:处理收到注册push成功返回的device token:

    NSLog(@"push deviceToken:%@",deviceToken);

    pushHelper::sharedPushHelper()->applicationDidRegisterForRemoteNotificationsWithDeviceToken([[deviceToken description] cStringUsingEncoding:NSUTF8StringEncoding]);

3.在application: didReceiveRemoteNotification: 处理app运行状态下接收到的Push消息:

    NSLog(@"Receive Notify: %@", [userInfo description]);
    [application setApplicationIconBadgeNumber:0];

    pushHelper::sharedPushHelper()->applicationDidReceiveRemoteNotification([[userInfo description] cStringUsingEncoding:NSUTF8StringEncoding]);

4.在application: didFailToRegisterForRemoteNotificationsWithError:处理注册push失败

    pushHelper::sharedPushHelper()->applicationdidFailToRegisterForRemoteNotificationsWithError([[error description] cStringUsingEncoding:NSUTF8StringEncoding]);
  • 实现pushHelper.cpp

在cocos2dx 3.0的版本中 CCNotificationCenter 被废弃。我们 使用自定义事件进行推送通知派发。

bool pushHelper::applicationDidFinishLaunchingWithNotification(const char* notificationJson)
{
    CCLOG("applicationDidFinishLaunchingWithNotification=%s",notificationJson);

    dispatcherNotificationEvent(notificationJson, NOTIFICATION_EVENT);

    return true;
}

void pushHelper::applicationDidRegisterForRemoteNotificationsWithDeviceToken(const char *deviceToken)
{
    CCLOG("applicationDidRegisterForRemoteNotificationsWithDeviceToken=%s",deviceToken);

    dispatcherNotificationEvent(deviceToken, REGISTER_NOTIFICATION_DEVICETOKEN_EVENT);
}

void pushHelper::applicationdidFailToRegisterForRemoteNotificationsWithError(const char *error)
{
    CCLOG("FailToRegisterForRemoteNotificationsWithError=%s",error);

    dispatcherNotificationEvent(error, REGISTER_NOTIFICATION_ERROR_EVENT);
}

void pushHelper::applicationDidReceiveRemoteNotification(const char* notificationJson)
{
    CCLOG("applicationDidReceiveRemoteNotification=%s",notificationJson);

    dispatcherNotificationEvent(notificationJson, NOTIFICATION_EVENT);
}

void pushHelper::dispatcherNotificationEvent(const char* data, const char* notificationEventType)
{
    auto director = Director::getInstance();
    char* buf = new char[256];
    sprintf(buf, "%s", data);
    EventCustom event(notificationEventType);
    event.setUserData(buf);
    director->getEventDispatcher()->dispatchEvent(&event);
    CC_SAFE_DELETE_ARRAY(buf);
}

push测试

push 消息的推送 需要客户端和服务器的支持。 自己搭建推送服务器(对于没有服务器编程经验的人)比较麻烦。 现在国内主流的第三方push方案(百度云推送、极光推送、个推),都提供push集成客户端SDK 和 push消息推送控制台 或 消息推送服务SDK,推送结果统计。根据应用ID注册,消息推送条数 决定收费。开发者可以灵活选择使用。

在此,本demo使用免费百度云推送方案,使用百度云推送的推送控制台测试。

  • 集成百度云推送
    1. 百度开发者注册工程 img
    2. 参照文档完成客户端SDK的集成,加入baidu 云推送 依赖的framework
    3. 完善工程配置,develop / production选择,上传APNS证书 img
    4. 使用百度云推送控制台推送push img

push使用

在想使用推送通知消息的地方加入监听推送通知事件的代码:

  • 在HelloWorldScene.cpp中监听remoteNotification
//listen & handle push message
void HelloWorld::onEnter()
{
    addNotificationListener();
}

void HelloWorld::onExit()
{
    removeNotificationListener();
}

void HelloWorld::addNotificationListener()
{
    notification_listener =  EventListenerCustom::create(NOTIFICATION_EVENT, [=](EventCustom* event){
        char* buf = static_cast<char*>(event->getUserData());
        CCLOG("Notification=%s",buf);
    });
    _eventDispatcher->addEventListenerWithFixedPriority(notification_listener, 1);


    register_notification_deviceToken_listener =  EventListenerCustom::create(REGISTER_NOTIFICATION_DEVICETOKEN_EVENT, [=](EventCustom* event){
        char* buf = static_cast<char*>(event->getUserData());
        CCLOG("register notification deviceToken=%s",buf);
    });
    _eventDispatcher->addEventListenerWithFixedPriority(register_notification_deviceToken_listener, 1);


    register_notification_error_listener =  EventListenerCustom::create(REGISTER_NOTIFICATION_ERROR_EVENT, [=](EventCustom* event){
        char* buf = static_cast<char*>(event->getUserData());
        CCLOG("register notification error=%s",buf);
    });
    _eventDispatcher->addEventListenerWithFixedPriority(register_notification_error_listener, 1);

}

void HelloWorld::removeNotificationListener()
{
    _eventDispatcher->removeEventListener(notification_listener);
    _eventDispatcher->removeEventListener(register_notification_deviceToken_listener);
    _eventDispatcher->removeEventListener(register_notification_error_listener);
}
  • push运行结果
    1. 获取device Token成功 绑定百度云推送成功 img
    2. 收到push消息 img
    3. 系统通知栏显示 imgimg

后记

本demo 基于3.0 rc2版本实现,但是使用其他版本集成push功能与之类似,没有太大变化。 开发者可以灵活选择。 push 消息的处理 和 使用第三方推送SDK集成也可以灵活选择。 完整资源下载地址

FAQ

  1. push 不能获取device Token "未找到aps environment 的权利字串"。 可能原因: 没有配置push 证书,App id 没有配置push。
  2. push 不能再 模拟器 和 已经越狱的机子上工作
  3. 收不到push 的可能原因: 根据devlop or production 模式选取 对应的APNS push证书配置和profile. push 受网络因素影响,请检查网络连接是否畅通。 push存在一定的延时

相关资料

  1. iOS RemoteNotifications Programming Guide
  2. 百度云推送
  3. 极光push
  4. 个推
  5. ios push证书设置