实现 APNs 离线推送

更新时间: 2023/07/21 06:54:54

前期准备

创建请求证书

  • 在 OSX 系统中点击“钥匙串访问”,生成请求证书。

  • 在证书信息中填入常用邮件地址,选择保存到磁盘。

创建应用AppID

  • 登陆 iOS Dev Center 选择进入 Certificates,Identifiers & Profiles。
  • 点击左栏 iOS Apps IDs ,进入 Identifiers - App IDs 列表
  • 点击右侧 “+” 号,创建 App ID

开启推送功能

  • 点击创建好的 App,选择 Edit 按钮。

  • 勾选 Push Notifications 功能,点击配置证书按钮,进入配置证书页面。请注意开发证书的种类,开发证书供开发调试使用,生产证书供发布使用。

配置证书

  • 将刚刚生成的请求证书(CSR文件)上传,点击 Generate。
  • 完成后下载并打开证书,会将证书自动导入钥匙串。

    在“钥匙串访问中”的“我的证书”里可以找到刚刚导入的证书。

导出推送所需p12文件

  • 选择刚刚导进来的证书,选择右键菜单中的导出选项。
  • 保存 p12 文件时,请设置密码,上传证书时需要填写密码。

证书上传

  • 进入网易云信控制台,选择对应的应用,进入证书管理->iOS推送证书
  • 按图中所示,上传刚刚导出的 p12 文件。可以上传多个 p12 文件,以上传时所填入的证书名称做区分。
  • ⽣产环境(线上环境)或 开发环境(测试环境),请选择与该证书⽣成时相同的环境类型。

客户端配置

配置推送证书

在 Android 和 iOS 平台中,推送证书名长度不超过 32 个字符,否则登录时会报错 500。

NIMSDKOption中设置对应的推送证书名。

objc- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    ...
    NSString *appKey        = @"your app key";
    NIMSDKOption *option    = [NIMSDKOption optionWithAppKey:appKey];
    option.apnsCername      = @"your APNs cer name";
    [[NIMSDK sharedSDK] registerWithOption:option];
    ...
}

上传devicetoken

客户端还需要将devicetoken上传至云信服务器用于后续的APNs推送,需要使用以下接口:

objc@interface NIMSDK : NSObject
/**
* @param token  当前设备的 devicetoken 
* @param key    自定义本端推送内容, 设置key可对应业务服务器自定义推送文案; 传@""清空配置, nil 则不更改

- (NSString *)updateApnsToken:(NSData *)token 
             customContentKey:(nullable NSString *)key
}

注:customContentKey的相关内容详见设置推送文案中的自定义默认推送文案部分。

示例代码:

objc- (void)registerPushService
{
    if (@available(iOS 11.0, *))
    {
        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
        [center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError * _Nullable error) {
            if (!granted)
            {
                dispatch_async_main_safe(^{
                    [[UIApplication sharedApplication].keyWindow makeToast:@"请开启推送功能否则无法收到推送通知" duration:2.0 position:CSToastPositionCenter];
                })
            }
        }];
    }
    else
    {
        UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types
                                                                                 categories:nil];
        [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
    }
    
    [[UIApplication sharedApplication] registerForRemoteNotifications];
    
  
    // 注册push权限,用于显示本地推送
    [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
}

...

- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    // 上传devicetoken至云信服务器。
    [[NIMSDK sharedSDK] updateApnsToken:deviceToken];
}

全局免打扰

NIM SDK 提供全局 APNs 属性设置,用于设置全局免打扰时间,详细内容可以参考 NIMPushNotificationSetting

  • 获取推送免打扰设置:
objcNIMPushNotificationSetting *setting = [[[NIMSDK sharedSDK] apnsManager] currentSetting];
  • 修改推送设置:

注意:请务必连同设置开始和结束时间。

objcNIMPushNotificationSetting *setting = [NIMSDK sharedSDK].apnsManager.currentSetting;
// 开启免打扰开关
setting.noDisturbing = YES;
setting.noDisturbingStartH = s_hour;
setting.noDisturbingStartM = s_minute;
setting.noDisturbingEndH = e_hour;
setting.noDisturbingEndM = e_minute;
[[[NIMSDK sharedSDK] apnsManager] updateApnsSetting:setting  
                                         completion:^(NSError *error) {}];

多端推送策略配置

当桌面端在线时,SDK 支持设置是否需要发推送给手机端。

objc@protocol  NIMApnsManager <NSObject>
/**
* 获取多端推送策略配置
*/
- (nullable NIMPushNotificationMultiportConfig *)currentMultiportConfig;

/**
* 设置多端推送策略配置
*/
- (void)updateApnsMultiportConfig:(NIMPushNotificationMultiportConfig *)config 
                       completion:(nullable NIMApnsHandler)completion;
@end

NIMPushNotificationMultiportConfig参数一览:

参数 类型 说明
shouldPushNotificationWhenPCOnline BOOL 桌面端在线时是否需要发送推送给手机端,默认为 YES,即需要推送,桌面端包括 PC,web , macOS 等…

消息推送配置

设置消息是否需要推送

当需要指定某条消息无需推送时,可以通过NIMMessage - NIMMessageSetting - apnsEnabled来设置。

示例代码:

objcNIMMessage *message = [[NIMMessage alloc] init];
message.text = @"消息示例";
NIMMessageSetting *setting = [[NIMMessageSetting alloc] init];
setting.apnsEnabled = NO; //设置为 无需推送
message.setting = setting;

设置推送文案

当需要自定义APNs推送文案(通知栏现实的文案)时,可以通过NIMMessage - apnsContent来设置。如果不设置 apnsContent 属性,将使用云信内置文案。

属性原型

objc@interface NIMMessage : NSObject
/**
 *  消息推送文案,长度限制500
 */
@property (nullable,nonatomic,copy)                  NSString *apnsContent;
@end

示例代码:

objcNIMAudioObject *audioObject = [[NIMAudioObject alloc] initWithSourcePath:filePath];
NIMMessage *message = [[NIMMessage alloc] init];
message.messageObject = audioObject;
message.apnsContent = @"发来了一段语音";    //对方收到的推送文案 

此时,对方的手机将会收到一条苹果推送,内容形式为 "昵称:"+"推送文案"。其中,昵称为推送文案前缀。

  • 自定义默认推送文案

如果开发者配置了「不显示推送详情」(配置方法见下),默认的推送文案是:“你收到一条新消息”。此时,开发者可以自定义推送文案(例如基于系统语言等场景)。注意:需要联系商务顾问申请开通才能使用。

登录云信控制台 - 选择应用 - 证书管理 - 自定义推送文案。可以配置最多 100 种自定义文案,每种自定义文案用一个自定义类型来标识。

当客户端配置为「不显示推送详情」时,自定义推送文案才会生效。

示例代码:

objc// 获取当前设置
NIMPushNotificationSetting *setting = [NIMSDK sharedSDK].apnsManager.currentSetting;
// 设置为不显示推送详情
setting.type = NIMPushNotificationDisplayTypeNoDetail;
[[[NIMSDK sharedSDK] apnsManager] updateApnsSetting:setting completion:^(NSError *error) {}];
objc- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    // 上传devicetoken时携带自定义推送文案类型。
    [[NIMSDK sharedSDK] updateApnsToken:deviceToken
                       customContentKey:@"customkey"];
}

设置推送文案前缀

可以通过NIMMessage - NIMMessageSetting - apnsWithPrefix来设置是否需要推送文案前缀。云信服务器向APNs请求推送时,前缀为用户昵称。

示例代码:

objcNIMMessage *message = [[NIMMessage alloc] init];
message.text = @"消息示例";
message.apnsContent = @"推送消息示例";    //对方收到的推送文案 
NIMMessageSetting *setting = [[NIMMessageSetting alloc] init];
setting.apnsWithPrefix = NO;           //不需要前缀
message.setting = setting;

角标未读数

角标未读数赋值

默认情况下,每一条消息的推送会让应用角标未读数加 1 。 如当应用处于后台时,收到5条消息推送,则应用的角标未读数增加5。

消息的发送方在发送消息时,可以通过NIMMessage - NIMMessageSetting - shouldBeCounted设置该消息推送时是否要计入角标未读数。

示例代码:

objcNIMMessage *message = [[NIMMessage alloc] init];
message.text = @"没有未读数的消息示例";
NIMMessageSetting *setting = [[NIMMessageSetting alloc] init];
setting.shouldBeCounted = NO;    //推送时不需要计入角标未读数
message.setting = setting;

苹果推送所附带的未读数是通过设置推送 payload 里的 badge 参数。在云信 服务中,不支持直接设置 badge ,服务器会维护对每个用户维护一个当前未读数,当服务器收到一条未读消息后,会自动在未读数上累加后填入推送的 badge 字段推送给接收端。

  • 手动赋值本地角标未读数

开发者应该在程序每次推到后台时,统计本地所有未读,并设置 badge , 保证程序在前后台未读数一致。例如:现在app在后台收到一条消息,此时app的角标会变成1;app回到前台,会话的总未读数是1,此时又收到一条消息,会话的总未读数变成2。当app再次切到后台时,开发者应调用iOS系统方法给角标赋值为2。

示例:

objc- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSInteger count = [[[NIMSDK sharedSDK] conversationManager] allUnreadCount];
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:count];
}
  • 自定义云信badge数值

如果应用本身除了云信未读之外还有其他未读的话,程序上的 icon 值应为 云信未读数 + 其他未读数 之和,但是由于云信服务器默认维护的未读数只有消息的未读数,这样会导致新来的消息推送未读数将不计入应用的其他未读。

这种情况下,需要客户端提交服务器应该管理的未读数。

原型

objc@protocol NIMApnsManager <NSObject>
/**
 *  注册获取 badge 数量的回调函数
 *
 *  @param handler 获取 badge 回调
 */
- (void)registerBadgeCountHandler:(NIMBadgeHandler)handler;
@end

如需注册此回调函数,建议的时机是在初始化SDK([[NIMSDK sharedSDK] registerWithOption:option])之后,就进行注册。并在回调中返回一个全局变量,后续在执行程序期间,按需修改此全局变量即可。SDK会自动去读取其中的返回值并传递给云信服务器。

角标未读数上限

云信侧发起的推送可以配置iOS应用角标未读数上限,配置位置:云信控制台 > 选择应用 > IM专业版/IM免费版 > 功能配置 > iOS应用角标未读数上限配置

推送铃声设置

消息推送的声音设置支持自定义,类似于推送的 payload , 开发者只需设置NIMMessage - apnsPayload,在其中插入一对键为sound的键值对。需要注意推送音频的具体格式,具体请参考苹果官方文档

示例

objcNIMMessage *message = [[NIMMessage alloc] init];
message.text = @"消息示例";
message.apnsContent = @"发来了一条信息";
message.apnsPayload = @{@"sound":@"message.wav"};

推送标题设置

目前云信发起的APNs推送支持两种方式设置推送标题:

  • 设置NIMMessage - apnsPayload - apsField - alert中的 titlebody两个key可以指定推送标题与推送文案,此优先级最高。特别地,如果配置了apsFieldalert字段,但是title为空或没有配置将没有推送标题,body为空或没有配置将没有推送文案,两者都为空或都没有配置则推送标题和推送文案都没有。具体可见下方示例。
  • 如果没有在apsField中配置,但是通过payload配置了"pushTitle": "标题内容",则以此显示。
  • 如果都没有配置,则不会显示推送标题。

开发者无法在推送参数payload中配置aps字段,IM提供推送配置payload - apsField字段,对应APNs的payload - aps字段。该字段仅适用于iOS接收,Android无法解析此字段。下面展示通过IM配置的带有apsField字段的payload:

json{
    // 上述示例代码出于展示目的进行了格式化,实际发送的时候不要进行格式化。
    "key1": "value1", //自定义键值对
    "apsField": {
        "mutable-content": 1,
        "sound": "abc.wav",
        "alert": {
            "title": "推送标题",
            "body": "推送内容"
        },
        "key2": "value2"
    }
}

群消息强制推送

用户在发消息的时候,可以通过配置 NIMMessage 里的 apnsMemberOption 字段实现群消息强制推送。即接收者屏蔽了当前会话(如免打扰),仍能够推送当前这条推送。

objc@interface NIMMessageApnsMemberOption : NSObject
/**
* 需要特殊推送的用户列表。如果这个字段为nil,则表示推送给当前会话内的所有用户。
*/
@property (nullable, nonatomic, copy) NSArray<NSString*> *userIds

/**
* 默认为 YES. 设置为 YES 表示即使推送列表中的用户屏蔽了当前会话(如免打扰),仍能够推送当前这条推送内容给相应用户
*/
@property (nonatomic, assign) BOOL forcePush

/**
* 推送文案,长度限制500字
*/
@property (nullable, nonatomic, copy) NSString *apnsContent
@end

点击通知栏跳转

当收到APNs推送后,用户点击通知栏后,希望进入指定的聊天界面时,可以参考如下方案:

发送方在构造消息对象时,在NIMMessage - apnsPayload中插入表示会话标识的信息(如自身的账号、群id、会话类型等),便于接收端获取到APNs - payload时解析。如:

json{
    // 上述示例代码出于展示目的进行了格式化,实际发送的时候不要进行格式化。
    "sessionid": "zhangsan", //表明该条消息的发送者的账号或者消息所属的群组id
    "sessiontype": "p2p",   //表明条消息对应的会话是单聊还是群聊等等
    "apsField": {
        "mutable-content": 1,
        "sound": "abc.wav",
        "alert": {
            "title": "推送标题",
            "body": "推送内容"
        },
        "key2": "value2"
    }
}

当接收端收到APNs推送时,解析APNs - payload得到sessionidsessiontype,并跳转到对应的界面。

通知栏内容覆盖

iOS 10及以上支持推送消息覆盖,即后一条推送内容覆盖前面推送内容。一种典型的场景是撤回消息的情况:

A发消息给B,产生APNs推送,文案内容为“你好“。然后A撤回了这条消息,此时通知栏中的“你好”变为预设的“对方撤回了一条消息”。

实现方式:可以前往 云信控制台-选择对应的应用-功能配置-消息撤回配置-撤回消息是否覆盖原始消息推送 进行设置。

此文档是否对你有帮助?
有帮助
去反馈
  • 前期准备
  • 创建请求证书
  • 创建应用AppID
  • 开启推送功能
  • 配置证书
  • 导出推送所需p12文件
  • 证书上传
  • 客户端配置
  • 配置推送证书
  • 上传devicetoken
  • 全局免打扰
  • 多端推送策略配置
  • 消息推送配置
  • 设置消息是否需要推送
  • 设置推送文案
  • 设置推送文案前缀
  • 角标未读数
  • 角标未读数赋值
  • 角标未读数上限
  • 推送铃声设置
  • 推送标题设置
  • 群消息强制推送
  • 点击通知栏跳转
  • 通知栏内容覆盖