远程推送

    本文主要介绍了使用融云 IM 时,何时会收到远程推送、如何使用远程推送、如何获取远程推送的内容。

    何时会收到远程推送

    在使用远程推送之前,您需要先了解融云 SDK 的运行状态。

    融云SDK的运行状态

    融云 SDK 根据 iOS App 运行的特性,主要有以下三种运行状态:

    1、 前台状态 如字面意思,App 前台可见时 SDK 处于前台状态。此时 App 使用融云的长连接通道来收发消息.

    2、 后台活动状态 当 App 进入后台 2 分钟之内,SDK 处于后台活跃状态。此时 App 使用融云的长连接通道接收消息。

    如果您使用 IMKit ,此时 SDK 收到消息会弹出本地通知(必须实现用户信息提供者和群组信息提供者,否则将不会有本地通知提示弹出)。

    如果您使用 IMLib ,此时 SDK 不会弹出本地通知,如果您需要可以自己弹出本地通知提示。

    3、 后台暂停状态当 App 进入后台 2 分钟之后或被杀进程或被冻结,SDK 将处于后台暂停状态。此时融云的长连接通道会断开,融云 Server 会通过 APNs 将消息以远程推送的形式下发到客户端。 此状态下如果有人给该用户发送消息,融云的服务器会根据 deviceToken 和推送证书将消息发送到苹果推送服务器,苹果服务器会将该消息推送到客户端。

    由于本地通知和远程推送表现形式类似,不易区分,您在调试时可以通过杀进程来测试远程推送,App 刚进入后台测试本地通知。
    当 SDK 处于后台暂停状态时,App 会收到远程推送。

    消息远程推送的流程

    当 SDK 处于后台暂停状态时,并且已经上传了 deviceToken ,这时候如果有人给该用户发送消息,融云的服务器会根据 deviceToken 和推送证书将消息发送到苹果推送服务器(APNs)。 苹果推送服务器会根据 deviceToken 查找相应的设备,并根据推送证书中的 BundleIDApp 打包时使用的 Provisioning Profile 查找 App,从而确定唯一的设备上的唯一 App,并进行远程推送。

    App 为前台状态和后台活跃状态时:

    Client A/App Server -> RongCloud Server: 发送消息至融云服务端。 RongCloud Server -> Client B: 融云服务端将消息发送给用户。

    App 为后台暂停状态时:

    Client A/App Server -> RongCloud Server: 发送消息至融云服务端。 RongCloud Server -> APNS: 当前通信所使用的 AppKey 对应的 P12 证书和目标用户 deviceToken。 APNS -> Client B: P12 证书对应 BundleID 的 App + 对应 Provision profile 的 App + deviceToken 对应的设备。

    开发环境与生产环境

    苹果推送服务器区分开发环境(Development)和生产环境(Production),两个环境的服务器不同,使用的 P12 证书不同,完全隔离。

    有几点需要注意的。

    • deviceToken 是唯一标识客户端的凭证,所以必须上传应用服务器才能使用远程推送。
    • 模拟器收不到远程推送。
    • 越狱的设备 APNs 服务不能保证,所以不一定能收到远程推送。
    • APNs 使用 BundleID 区分 App,使用通配符 BundleID 的 App 将无法使用远程推送。

    如何使用远程推送

    1. 为 App 开启远程推送服务

    登录 Apple Developer,进入 Identifiers 选择 App IDs。

    image


    image

    您可以新建一个 AppID,或者在您原有的 AppID 上增加 Push Notification 的 Service。 需要注意的是,您 App 的 BundleID 不能使用通配符,否则将无法使用远程推送服务。

    image

    开启远程推送服务。

    image

    2. 生成并上传 P12 证书

    选中您的 AppID ,选择 Edit。

    image

    您可以看见,在 Push Notification 下方有两个 SSL Certificate ,分别是用于开发环境和生产环境的远程推送证书。

    image

    点击 Create Certificate ,这时候会提示您需要一个 Certificate Signing Request(CSR)。

    image

    您可以根据其说明,在 Mac 上打开钥匙串应用,在菜单中点击“从证书颁发机构请求证书”。

    image

    输入您的邮箱、姓名或公司名,选择保存到磁盘,点击继续,就会生成一个 *.certSigningRequest 文件。

    image

    然后返回 Apple Developer 网站,点击 Continue,上传生成的 .certSigningRequest 文件,点击 Generate ,即可生成推送证书。

    image

    按照上面同样的步骤,生成生产环境的推送证书。

    image
    从 iOS 9.2开始,Apple Developer 上生成的生产环境推送证书,名称为 Apple Push Services: XXX, 之前生成的生产环境推送证书名称为 Apple Production IOS Push Services: XXX

    此时,您可以在 Push Notification 下方看见目前每个环境对应的推送证书。

    image

    将上面的 SSL Certificate 都下载到 Mac 本地,双击打开,系统会将其导入钥匙串中。 打开钥匙串应用,选中对应的证书,右键选择导出。保存 P12 文件时,可以为其设置密码,也可以不设置密码。

    image


    image

    3. 融云开发者平台上传 p12 证书

    在融云开发者平台,您需要在 App -> IM 服务 -> 应用标识 路径下,填写 iOS Bundle Identifier,并在对应的环境下上传刚才导出的 P12 证书。

    image

    需要注意几点:

    • Bundle Identifier 必须填写,必须与您工程设置的 BundleID 一致。
    • 您上传的 P12 证书必须与环境相匹配。开发环境必须上传 DevelopmentPush 证书,生产环境必须上传 Production 的 Push 证书。

    4. 生成 provisioning profile 文件

    Xcode 编译打包的时候,需要使用使用 provisioning profile 来指明 AppID 、证书和设备。

    而远程推送与证书、设备相关,所以必须正确设置相应环境的 provisioning profile,否则将无法收到远程推送,点击查看原因说明

    Apple Developer 中,选择 Provisioning Profile,选择生成开发环境或者生产环境的 provisioning profile

    image

    选择对应的 AppID

    image

    选择允许使用的开发者证书。

    image

    选择允许使用的设备。

    image

    输入要名称即可生成。

    image


    image

    如果您的 App 是 App-Store 类型的,但是还没有在 App Store 上架,在上架之前如果您想使用生产环境测试远程推送,您需要生成一个 Ad-Hoc 的 Provisioning Profile ,并在 Xcode 中使用其进行打包,导出为 Ad Hoc Deployment

    更多内容,可以参考:苹果文档 - Exporting Your App for Testing

    image

    5. 在 Xcode 设置 provisioning profile

    将您生成的 provisioning profile 下载到 Mac 本地,双击打开,则 Xcode 默认会将其导入。 在您 Xcode 工程中,Targets -> Build Settings -> Code Signing -> Provisioning Profile 路径下,选择刚才下载的证书。

    image

    Debug 模式下选择 dev 的 provisioning profileRelease 模式下选择 distribution 的 provisioning profile。 Xcode 在 run 的时候默认使用 Debug 模式,在 Archive 的时候默认使用 Release 模式,您如果需要也可以修改。

    image


    image

    需要注意的是,无论是 DebugRelease,您需要确保在打包/运行时使用的 provisioning profile 必须和您正在使用的 Appkey 环境一致,才能收到推送。

    6. 在代码中请求远程推送权限

    向用户请求允许推送。

        // AppDelegate class
    
        /**
         * 推送处理1
         */
        if ([application
                respondsToSelector:@selector(registerUserNotificationSettings:)]) {
          //注册推送, 用于iOS8以及iOS8之后的系统
          UIUserNotificationSettings *settings = [UIUserNotificationSettings
              settingsForTypes:(UIUserNotificationTypeBadge |
                                UIUserNotificationTypeSound |
                                UIUserNotificationTypeAlert)
                    categories:nil];
          [application registerUserNotificationSettings:settings];
        } else {
          //注册推送,用于iOS8之前的系统
          UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge |
                                             UIRemoteNotificationTypeAlert |
                                             UIRemoteNotificationTypeSound;
          [application registerForRemoteNotificationTypes:myTypes];
        }
    
        /**
         * 推送处理2
         */
        //注册用户通知设置
        - (void)application:(UIApplication *)application
            didRegisterUserNotificationSettings:
                (UIUserNotificationSettings *)notificationSettings {
          // register to receive notifications
          [application registerForRemoteNotifications];
        }
    

    设置deviceToken。

        // AppDelegate class
    
        /**
         * 推送处理3
         */
        - (void)application:(UIApplication *)application
            didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
          NSString *token =
              [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<"
                                                                     withString:@""]
                  stringByReplacingOccurrencesOfString:@">"
                                            withString:@""]
                  stringByReplacingOccurrencesOfString:@" "
                                            withString:@""];
    
          [[RCIMClient sharedRCIMClient] setDeviceToken:token];
        }
    

    deviceToken 是系统提供的,从苹果服务器获取的,用于 APNs 远程推送必须使用的设备唯一值。

    您需要将 didRegisterForRemoteNotificationsWithDeviceToken: 获取到的 deviceToken ,转为 NSString 类型,并去掉其中的空格和尖括号,传给 SDK。

    SDK 会将此 deviceToken 保存,在 Connect 时会自动上传到融云服务器,并与用户 ID 绑定,用于远程推送。

    7. 用户允许推送

    以上步骤都已经实现后,还需要使用您 App 的用户允许通知,才能收到远程推送。您可以在设备的设置应用中,查看当前App是否允许通知。

    如何获取远程推送的内容

    远程推送的格式

    融云消息的远程推送内容格式如下:

        {
          "aps" :
              {
                 "alert" : "You got your emails.",
                 "badge" : 1,
                 "sound" : "default"
              },
           "rc":{"cType":"PR","fId":"xxx","oName":"xxx","tId":"xxxx"},
           "appData":"xxxx"
        }
    

    rc 数据说明:

    名称 类型 说明
    alert String 远程推送显示的内容。自带的消息会有默认显示,如果您使用的是自定义消息,需要在发送时设置。对应于 iOS 发送消息接口中的 pushContent。
    cType String 会话类型。PR 指单聊、 DS 指讨论组、 GRP 指群组、 CS 指客服、SYS 指系统会话、 MC 指应用内公众服务、 MP 指跨应用公众服务。
    fId String 消息发送者的用户 ID。
    oName String 消息类型,参考融云消息类型表.消息标志;可自定义消息类型。
    tId String Target ID。
    appData String 远程推送的附加信息,对应于 iOS 发送消息接口中的 pushData。

    远程推送内容的获取

    点击通知栏的远程推送时,如果此时 App 已经被系统冻结,则您在 AppDelegate-application:didFinishLaunchingWithOptions: 中可以捕获该消息。

        // AppDelegate class
    
        - (BOOL)application:(UIApplication *)application
            didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
            // 远程推送的内容
            NSDictionary *remoteNotificationUserInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
    
            ...
    
            return YES;
        }
    

    如果 App 未被系统冻结,则您在 AppDelegate-application:didReceiveRemoteNotification: 中可以捕获该消息。

        // AppDelegate class
    
        - (void)application:(UIApplication *)application
            didReceiveRemoteNotification:(NSDictionary *)userInfo {
            // userInfo为远程推送的内容
        }
    

    VoIP 推送设置

    使用和设置 VoIP 推送之前,必须设置和开启普通消息的远程推送。具体可以参考如何使用远程推送

    1. 何时需要设置 VoIP 推送

    如果您使用了融云通话 SDK(CallKit 或 CallLib),则需要开启 VoIP 推送功能并上传 VoIP 推送证书。

    2. 为 App 开启推送服务

    首先,您需要在 Apple Developer 为您的 App 开启远程推送服务。 具体步骤可以参考文档

    3. 生成 P12 证书

    点击 “Certificates -> All” ,在 iOS Certificates 界面,点击右上角的加号。

    image

    选择 Production -> VoIP Services Certificate,点击 “Continue”。

    image

    选择您应用对应的 App ID,点击 “Continue”。

    image

    点击 "Create Certificate",这时候会提示您需要一个 Certificate Signing Request(CSR)

    image

    您可以根据其说明,在 Mac 上打开钥匙串应用,在菜单中点击“从证书颁发机构请求证书”。

    image

    输入您的邮箱、姓名或公司名,选择保存到磁盘,点击继续,就会生成一个 *.certSigningRequest 文件。

    image

    然后返回 Apple Developer 网站,点击 "Continue",上传生成的 .certSigningRequest 文件,点击 “Generate”,即可生成推送证书。

    image

    将上面的 SSL Certificate 都下载到 Mac 本地,双击打开,系统会将其导入钥匙串中。 打开钥匙串应用,选中对应的证书,右键选择导出。保存 P12 文件时,可以为其设置密码,也可以让密码为空。

    image
    image

    4. 上传 P12 证书

    您需要在融云开发者平台 App -> IM 服务 -> 应用标识 路径下,选择生产环境,上传刚才导出的 P12 证书。

    image

    需要注意

    苹果规定只有生产环境才能使用 VoIP 推送,所以开发环境无法使用 VoIP 推送。

    5. 设置 provisioning profile

    在 Xcode 中设置 provisioning profile。

    因为苹果的 VoIP Push 只提供生产环境的证书,所以只有在生产环境才可以使用 VoIP 推送。

    所以您在打包的时候,需要设置 provisioning profile 为 distribution 才能收到 VoIP 推送。 关于 Xcode 如果设置 provisioning profile 的更多内容,可以参考文档

    6. App 开启后台 VoIP 通话设置

    您需要在工程设置中 Capabilities -> Background Modes ,勾选上 “Voice over IP” 和 “Remote Notifications”。

    image

    7. App 唤醒注意事项

    因为收到 VoIP 推送之后,App 在后台会被系统短暂唤醒。 为了正常处理通话相关逻辑,您需要在 AppDelegate 的 - application:didFinishLaunchingWithOptions: 方法中需要初始化融云 SDK 并连接服务器。

    推送测试的错误码及排查处理

    融云在开发者平台中提供了推送的测试工具,您可以在开发者后台 -> 具体应用 -> 应用标识 -> 测试 Push 路径下测试推送功能。 下面列举了推送测试过程中,可能遇上的错误码、原因以及需要进行的排查处理。

    错误码 原因 处理
    5, 8 deviceToken 信息有误。 请检查设备是否越狱,setDeviceToken 传入的参数是否正确(参考),Xcode 在打包时使用的provisioning profile是否与当前环境匹配(参考1参考2)。
    52 当前环境下 UserId 不存在,请确认一下是否连接服务器成功,init 使用的 App Key 是否是对应环境的 App Key。 connectWithTokensuccess Block 会回调当前登陆的 UserId ,您可以查一下和您测试 Push 输入的是否一致。
    53 您上传的推送证书为空。 检查并重新上传证书(参考1参考2参考3),并检查您填写的 BundleID 和证书中的 BundleID 是否对应(参考)。
    54, 62 您上传的证书或者证书密码有问题。 请检查您填写的证书密码,检查并重新上传证书(参考1参考2参考3),并检查您填写的 BundleID 和证书中的 BundleID 是否对应(参考)。
    55 该 userId 当前未在 iOS 设备上登录。 请检查是否在 iOS 设备上成功登陆,是否存在多端登陆同一个 userId 的情况。
    57 发送苹果 Push 失败。 SSL 解析异常,重新上传证书。
    2, 56 该 userId 没有设置 deviceToken 信息。 请检查设备是否越狱,用户是否允许 App 进行通知(参考),是否请求了远程推送权限以及 setDeviceToken 是否设置正确(参考)。
    58 原始参数解析失败。 请联系我们尽快解决问题。
    60 socket 异常。 内部错误,重新发送。
    61 该 userId 设置了屏蔽推送。 请检查客户端是否屏蔽了推送功能(如果您有多个客户端或者 App 使用同一个 Appkey,请检查一遍所有的客户端代码是否设置了屏蔽推送)。
    3, 4, 6, 7, 64 网络原因,推送失败,请您再次尝试。 网络原因,请重试。
    0, 1, 10, 255,59 Apple APNS 服务器服务异常。 请过一段时间再尝试。
    66 上传的证书与当前环境不匹配。 请检查并重新上传证书(参考1参考2参考3参考4)。
    67 您上传证书的密码错误,请核对您的证书和密码。 请检查证书和密码,重新上传证书或填写密码(参考1参考2)。
    68 开发者平台填写的Bundle ID和证书中的Bundle ID不匹配,请检查。 请检查填写的BundleID和证书Bundle ID。
    70 VoIP Push Token 为空。 请确认 VoIP Push Token 是否为空。
    73 没有用户信息,此用户 ID 没有获取过融云 Token。 重新获取用户 Token。
    74 上传证书包名与配置包名不一致。 请检查上传证书包名与配置包名是否一致。
    75 上传的证书包含的 BundleId 含有通配符。 按照苹果的要求,使用通配符的 App 无法使用APNs远程推送。
    1050 APNS 返回超时,您可能延时收到推送消息,也可能推送失败。 网络原因,请重试。

    常见问题

    为什么提示导出的 P12 证书的 Bundle ID 不对?

    参考说明文档