前言
最近公司的聊天消息增加一个离线消息推送的功能,之前因为排期的问题,只是简单的本地通知,在账号离线的状态下或者是没有开启程序的时候都不会收到,所以新加了一下这个功能。在做这个功能的时候遇到一些小问题,特别在这里分享一下,下面的内容大部分都是对腾讯云通讯文档的整理和分析。
一,集成步骤:
1,推送原理:
如想要接收APNs离线消息通知,需要在腾讯云管理平台提交Push证书,在客户端每次登录时,获取并通过API接口上报Token。详细推送原理可参阅:Apple Push Notification Service。(值得注意的是,推送功能只用于通知用户,如果APP在前台,以app内消息提示小红点为主,不会跳消息提示,didReceiveRemoteNotification 获取到的消息由于不可控,可以忽略。)
2,证书申请流程
APNs 证书申请流程可参考文档:Apple推送证书申请。(这里注意iOS10的推送的区别)。这里有一篇关于iOS10推送的介绍博客,可以看一下
3,上传证书到控制台
完成APNs 证书申请以后,需要把生成的p12证书上传到控制台:
上传时有几个注意点:
1,上传证书名最好使用全英文(尤其不能使用括号等特殊字符)
2,上传证书生效时间为10分钟左右
3,上传证书需要设置密码,无密码收不到推送(这个很重要,一定要设置证书密码,虽然我也不知道为什么要这么做...)
4,注意生产环境的选择,发布AppStore的证书需要设置为生产环境,否则无法收到推送
5,上传的p12证书必须是自己申请的真实有效的证书
4,客户端实现APNs推送 (重点代码演示)
4.1 步骤:
客户端要实现接收APNs推送,需要实现4个部分:
向苹果后台请求DeviceToken、
登录SDK后上传Token到腾讯云、
APP进入后台时上报切后台事件、
APP进入前台时上报切前台事件。
- (void)replyPushNotificationAuthorization:(UIApplication *)application{
if (IOS10_OR_LATER) {
//iOS 10 later
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
//必须写代理,不然无法监听通知的接收与点击事件
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionAlert | UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!error && granted) {
//用户点击允许
NSLog(@"注册成功");
}else{
//用户点击不允许
NSLog(@"注册失败");
}
}];
// 可以通过 getNotificationSettingsWithCompletionHandler 获取权限设置
//之前注册推送服务,用户点击了同意还是不同意,以及用户之后又做了怎样的更改我们都无从得知,现在 apple 开放了这个 API,我们可以直接获取到用户的设定信息了。注意UNNotificationSettings是只读对象哦,不能直接修改!
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
NSLog(@"========%@",settings);
}];
}else if (IOS8_OR_LATER)
{
//iOS 8 - iOS 10系统
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
[application registerUserNotificationSettings:settings];
}
//注册远端消息通知获取device token
[application registerForRemoteNotifications];
}
/**
* 在AppDelegate的回调中会返回DeviceToken,需要在登录后上报给腾讯云后台
/**
-(void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[[IMAPlatform sharedInstance] configOnAppRegistAPNSWithDeviceToken:deviceToken];
}
上传Token到腾讯云请参考如下代码,填写参数时busiId需要和控制台分配的证书ID保持一致:
- (void)configOnAppRegistAPNSWithDeviceToken:(NSData *)deviceToken
{
DebugLog(@"didRegisterForRemoteNotificationsWithDeviceToken:%ld", (unsigned long)deviceToken.length);
NSString *token = [NSString stringWithFormat:@"%@", deviceToken];
[[TIMManager sharedInstance] log:TIM_LOG_INFO tag:@"SetToken" msg:[NSString stringWithFormat:@"My Token is :%@", token]];
TIMTokenParam *param = [[TIMTokenParam alloc] init];
/* 用户自己到苹果注册开发者证书,在开发者账号中下载并生成证书(p12文件),将生成的p12文件传到腾讯证书管理控制台,控制台会自动生成一个证书id,将证书id传入一下busiId参数中。*/
#if kAppStoreVersion
// AppStore版本
#if DEBUG
param.busiId = 2383;
#else
param.busiId = 2382;
#endif
#else
//企业证书id
param.busiId = 2516;
#endif
[param setToken:deviceToken];
// [[TIMManager sharedInstance] setToken:param];
[[TIMManager sharedInstance] setToken:param succ:^{
NSLog(@"-----> 上传token成功 ");
} fail:^(int code, NSString *msg) {
NSLog(@"-----> 上传token失败 ");
}];
}
注意这里有个问题:这个上传token的步骤必须要在sdk登陆成功之后,才能进行。所以我之前这个步骤都是写在sdk登陆成功之后的回调中...
上报切后台事件请参考如下代码:(下面的这个接口成功,才能收到推送)
- (void)applicationDidEnterBackground:(UIApplication *)application
{
__block UIBackgroundTaskIdentifier bgTaskID;
bgTaskID = [application beginBackgroundTaskWithExpirationHandler:^ {
//不管有没有完成,结束background_task任务
[application endBackgroundTask: bgTaskID];
bgTaskID = UIBackgroundTaskInvalid;
}];
[[IMAPlatform sharedInstance] configOnAppEnterBackground];
}
- (void)configOnAppEnterBackground
{
NSUInteger unReadCount = [[IMAPlatform sharedInstance].conversationMgr unReadMessageCount];
[UIApplication sharedApplication].applicationIconBadgeNumber = unReadCount;
TIMBackgroundParam *param = [[TIMBackgroundParam alloc] init];
[param setC2cUnread:(int)unReadCount];
[[TIMManager sharedInstance] doBackground:param succ:^() {
DebugLog(@"doBackgroud Succ");
} fail:^(int code, NSString * err) {
DebugLog(@"Fail: %d->%@", code, err);
}];
}
上报切前台事件请参考如下代码:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[TIMManager sharedInstance] doForeground:^() {
DebugLog(@"doForegroud Succ");
} fail:^(int code, NSString * err) {
DebugLog(@"Fail: %d->%@", code, err);
}];
}
5,推送格式
5.1 通用推送规则
对于单聊消息,APNs推送规则为:
昵称:内容
其中昵称是发送方用户昵称,如果未设置昵称,则只显示内容。
对于群聊消息,APNs 推送规则为:
名称(群名):内容
其中名称为群名片或者发送者昵称,优先级为 群名片 > 群名。
5.2 不同类型消息推送规则
PNs 推送内容部分为消息体中各个Elem内容组合:
文本Elem:直接显示内容
语音Elem:显示 [语音]
文件Elem:显示 [文件]
图片Elem:显示 [图片]
自定义Elem:显示desc字段内容
例如:
一条消息中包含 文本Elem和图片Elem,文本内容为Test,最终显示的内容为: Test[图片]
另外一条消息中内容为空,则不进行下发,例如如果消息中只有自定义Elem,并且desc为空,则不进行下发;
这里不用关心是否托管账号还是独立账号,只要设置了昵称或者群名推送消息就会带上。
5.3多APP支持
对于需要多APP互通的场景,可在多个APP中写同一个sdkappid,可实现消息互通,由于多个APP推送证书不同,所以需要在控制台上提交多个证书,每个证书在IM通讯云上生成一个编号,可参考 3.1 Token 上报 设置证书,并提供当前证书的编号。
6, 推送声音
6.1 设置自己的推送声音
不同用户可能想使用不通的推送声音,sdk提供了设置用户声音的接口,可实现单聊声音、群组声音、音视频(暂不支持)声音的设置,也可在用户级别设置是否接收推送。
* APNs 配置
*/
@interface TIMAPNSConfig : NSObject
/**
* 是否开启推送:0-不进行设置 1-开启推送 2-关闭推送
*/
@property(nonatomic,assign) uint32_t openPush;
/**
* C2C消息声音,不设置传入nil
*/
@property(nonatomic,retain) NSString * c2cSound;
/**
* Group消息声音,不设置传入nil
*/
@property(nonatomic,retain) NSString * groupSound;
/**
* Video声音,不设置传入nil
*/
@property(nonatomic,retain) NSString * videoSound;
@end
@interface TIMManager : NSObject
/**
* 设置APNS配置
*
* @param config APNS配置
* @param succ 成功回调
* @param fail 失败回调
*
* @return 0 成功
*/
-(int) setAPNS:(TIMAPNSConfig*)config succ:(TIMSucc)succ fail:(TIMFail)fail;
@end
参数说明:
参数 说明
config 设置配置
openPush : 是否开启推送 0-不进行设置 1-开启推送 2-关闭推送
c2cSound : 单聊声音,文件名
groupSound : 群组声音,文件名
videoSound : 音视频邀请声音,文件名
succ 成功回调
fail 失败回调
6.2 获取自己的推送声音
界面展示如果需要获取推送声音,可使用 getAPNSConfig 获取,此接口每次都从服务器同步数据,不会进行本地缓存。
@interface TIMManager : NSObject
/**
* 获取APNS配置
*
* @param succ 成功回调,返回配置信息
* @param fail 失败回调
*
* @return 0 成功
*/
-(int) getAPNSConfig:(TIMAPNSConfigSucc)succ fail:(TIMFail)fail;
@end
参数说明:
参数 说明
succ 成功回调,返回 TIMAPNSConfig 结构体
fail 失败回调
6.3 每条离线推送属性
如果需要定制每条消息的展示文本、扩展字段、提示音、是否推送属性,可以在消息设置TIMOfflinePushInfo,此条消息在推送时,会替换用户原有的默认属性。可实现每条消息定制化推送。填入kIOSOfflinePushNoSound到sound属性时接收端强制为静音提示
/**
填入sound字段表示接收时不会播放声音
*/
extern NSString * const kIOSOfflinePushNoSound;
@interface TIMAndroidOfflinePushConfig : NSObject
/**
* 离线推送时展示标签
*/
@property(nonatomic,retain) NSString * title;
/**
* Android离线Push时声音字段信息
*/
@property(nonatomic,retain) NSString * sound;
/**
* 离线推送时通知形式
*/
@property(nonatomic,assign) TIMAndroidOfflinePushNotifyMode notifyMode;
@end
@interface TIMIOSOfflinePushConfig : NSObject
/**
* 离线Push时声音字段信息
*/
@property(nonatomic,retain) NSString * sound;
/**
* 忽略badge计数
*/
@property(nonatomic,assign) BOOL ignoreBadge;
@end
@interface TIMOfflinePushInfo : NSObject
/**
* 自定义消息描述信息,做离线Push时文本展示
*/
@property(nonatomic,retain) NSString * desc;
/**
* 离线Push时扩展字段信息
*/
@property(nonatomic,retain) NSString * ext;
/**
* 推送规则标志
*/
@property(nonatomic,assign) TIMOfflinePushFlag pushFlag;
/**
* iOS离线推送配置
*/
@property(nonatomic,retain) TIMIOSOfflinePushConfig * iosConfig;
/**
Android离线推送配置
*/
@property(nonatomic,retain) TIMAndroidOfflinePushConfig * androidConfig;
@end
上面的配置一定要在每条发送的message中配置一下,要不然是不会接收到推送消息的。(就是因为功能是先做的,推送是后加的,之前版本的sdk相关的字段废弃了,所以没有配置,卡在了这里很多时间,确实需要检讨一下)
好了,差不多就是这么多了。本人的博客大部分都是项目中的遇到的自己遇到的一些问题,或者是自己无聊写的一些小东西,没有什么特别厉害的东西。把博客当做是日记一样写出来,主要是为了提高一下自己语言组织能力和问题记录的目的。。。。
大家加油!!!!