实现 APNs 离线推送
更新时间: 2024/11/25 10:11:32
前期准备
创建请求证书
-
在 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
中的title
和body
两个key可以指定推送标题与推送文案,此优先级最高。特别地,如果配置了apsField
的alert
字段,但是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
得到sessionid
和sessiontype
,并跳转到对应的界面。
通知栏内容覆盖
iOS 10及以上支持推送消息覆盖,即后一条推送内容覆盖前面推送内容。一种典型的场景是撤回消息的情况:
A发消息给B,产生APNs推送,文案内容为“你好“。然后A撤回了这条消息,此时通知栏中的“你好”变为预设的“对方撤回了一条消息”。
实现方式:可以前往 云信控制台-选择对应的应用-功能配置-消息撤回配置-撤回消息是否覆盖原始消息推送 进行设置。