前言
最近公司的聊天消息增加一個(gè)離線消息推送的功能,之前因?yàn)榕牌诘膯?wèn)題态辛,只是簡(jiǎn)單的本地通知麸澜,在賬號(hào)離線的狀態(tài)下或者是沒(méi)有開(kāi)啟程序的時(shí)候都不會(huì)收到,所以新加了一下這個(gè)功能奏黑。在做這個(gè)功能的時(shí)候遇到一些小問(wèn)題炊邦,特別在這里分享一下,下面的內(nèi)容大部分都是對(duì)騰訊云通訊文檔的整理和分析熟史。
一馁害,集成步驟:
1,推送原理:
如想要接收APNs離線消息通知蹂匹,需要在騰訊云管理平臺(tái)提交Push證書(shū)碘菜,在客戶端每次登錄時(shí),獲取并通過(guò)API接口上報(bào)Token限寞。詳細(xì)推送原理可參閱:Apple Push Notification Service忍啸。(值得注意的是,推送功能只用于通知用戶履植,如果APP在前臺(tái)计雌,以app內(nèi)消息提示小紅點(diǎn)為主,不會(huì)跳消息提示玫霎,didReceiveRemoteNotification 獲取到的消息由于不可控凿滤,可以忽略。)
2庶近,證書(shū)申請(qǐng)流程
APNs 證書(shū)申請(qǐng)流程可參考文檔:Apple推送證書(shū)申請(qǐng)翁脆。(這里注意iOS10的推送的區(qū)別)。這里有一篇關(guān)于iOS10推送的介紹博客拦盹,可以看一下
3鹃祖,上傳證書(shū)到控制臺(tái)
完成APNs 證書(shū)申請(qǐng)以后,需要把生成的p12證書(shū)上傳到控制臺(tái):
上傳時(shí)有幾個(gè)注意點(diǎn):
1,上傳證書(shū)名最好使用全英文(尤其不能使用括號(hào)等特殊字符)
2,上傳證書(shū)生效時(shí)間為10分鐘左右
3,上傳證書(shū)需要設(shè)置密碼,無(wú)密碼收不到推送(這個(gè)很重要恬口,一定要設(shè)置證書(shū)密碼校读,雖然我也不知道為什么要這么做...)
4,注意生產(chǎn)環(huán)境的選擇,發(fā)布AppStore的證書(shū)需要設(shè)置為生產(chǎn)環(huán)境祖能,否則無(wú)法收到推送
5,上傳的p12證書(shū)必須是自己申請(qǐng)的真實(shí)有效的證書(shū)
4歉秫,客戶端實(shí)現(xiàn)APNs推送 (重點(diǎn)代碼演示)
4.1 步驟:
客戶端要實(shí)現(xiàn)接收APNs推送,需要實(shí)現(xiàn)4個(gè)部分:
向蘋果后臺(tái)請(qǐng)求DeviceToken养铸、
登錄SDK后上傳Token到騰訊云雁芙、
APP進(jìn)入后臺(tái)時(shí)上報(bào)切后臺(tái)事件、
APP進(jìn)入前臺(tái)時(shí)上報(bào)切前臺(tái)事件钞螟。
- (void)replyPushNotificationAuthorization:(UIApplication *)application{
if (IOS10_OR_LATER) {
//iOS 10 later
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
//必須寫代理兔甘,不然無(wú)法監(jiān)聽(tīng)通知的接收與點(diǎn)擊事件
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionAlert | UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!error && granted) {
//用戶點(diǎn)擊允許
NSLog(@"注冊(cè)成功");
}else{
//用戶點(diǎn)擊不允許
NSLog(@"注冊(cè)失敗");
}
}];
// 可以通過(guò) getNotificationSettingsWithCompletionHandler 獲取權(quán)限設(shè)置
//之前注冊(cè)推送服務(wù),用戶點(diǎn)擊了同意還是不同意鳞滨,以及用戶之后又做了怎樣的更改我們都無(wú)從得知洞焙,現(xiàn)在 apple 開(kāi)放了這個(gè) API,我們可以直接獲取到用戶的設(shè)定信息了拯啦。注意UNNotificationSettings是只讀對(duì)象哦澡匪,不能直接修改!
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
NSLog(@"========%@",settings);
}];
}else if (IOS8_OR_LATER)
{
//iOS 8 - iOS 10系統(tǒng)
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
[application registerUserNotificationSettings:settings];
}
//注冊(cè)遠(yuǎn)端消息通知獲取device token
[application registerForRemoteNotifications];
}
/**
* 在AppDelegate的回調(diào)中會(huì)返回DeviceToken褒链,需要在登錄后上報(bào)給騰訊云后臺(tái)
/**
-(void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[[IMAPlatform sharedInstance] configOnAppRegistAPNSWithDeviceToken:deviceToken];
}
上傳Token到騰訊云請(qǐng)參考如下代碼唁情,填寫參數(shù)時(shí)busiId需要和控制臺(tái)分配的證書(shū)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];
/* 用戶自己到蘋果注冊(cè)開(kāi)發(fā)者證書(shū),在開(kāi)發(fā)者賬號(hào)中下載并生成證書(shū)(p12文件)甫匹,將生成的p12文件傳到騰訊證書(shū)管理控制臺(tái)甸鸟,控制臺(tái)會(huì)自動(dòng)生成一個(gè)證書(shū)id,將證書(shū)id傳入一下busiId參數(shù)中赛惩。*/
#if kAppStoreVersion
// AppStore版本
#if DEBUG
param.busiId = 2383;
#else
param.busiId = 2382;
#endif
#else
//企業(yè)證書(shū)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失敗 ");
}];
}
注意這里有個(gè)問(wèn)題:這個(gè)上傳token的步驟必須要在sdk登陸成功之后哀墓,才能進(jìn)行。所以我之前這個(gè)步驟都是寫在sdk登陸成功之后的回調(diào)中...
上報(bào)切后臺(tái)事件請(qǐng)參考如下代碼:(下面的這個(gè)接口成功喷兼,才能收到推送)
- (void)applicationDidEnterBackground:(UIApplication *)application
{
__block UIBackgroundTaskIdentifier bgTaskID;
bgTaskID = [application beginBackgroundTaskWithExpirationHandler:^ {
//不管有沒(méi)有完成,結(jié)束background_task任務(wù)
[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);
}];
}
上報(bào)切前臺(tái)事件請(qǐng)參考如下代碼:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[TIMManager sharedInstance] doForeground:^() {
DebugLog(@"doForegroud Succ");
} fail:^(int code, NSString * err) {
DebugLog(@"Fail: %d->%@", code, err);
}];
}
5后雷,推送格式
5.1 通用推送規(guī)則
對(duì)于單聊消息季惯,APNs推送規(guī)則為:
昵稱:內(nèi)容
其中昵稱是發(fā)送方用戶昵稱,如果未設(shè)置昵稱臀突,則只顯示內(nèi)容勉抓。
對(duì)于群聊消息,APNs 推送規(guī)則為:
名稱(群名):內(nèi)容
其中名稱為群名片或者發(fā)送者昵稱候学,優(yōu)先級(jí)為 群名片 > 群名藕筋。
5.2 不同類型消息推送規(guī)則
PNs 推送內(nèi)容部分為消息體中各個(gè)Elem內(nèi)容組合:
文本Elem:直接顯示內(nèi)容
語(yǔ)音Elem:顯示 [語(yǔ)音]
文件Elem:顯示 [文件]
圖片Elem:顯示 [圖片]
自定義Elem:顯示desc字段內(nèi)容
例如:
一條消息中包含 文本Elem和圖片Elem,文本內(nèi)容為Test梳码,最終顯示的內(nèi)容為: Test[圖片]
另外一條消息中內(nèi)容為空隐圾,則不進(jìn)行下發(fā)伍掀,例如如果消息中只有自定義Elem,并且desc為空暇藏,則不進(jìn)行下發(fā)蜜笤;
這里不用關(guān)心是否托管賬號(hào)還是獨(dú)立賬號(hào),只要設(shè)置了昵稱或者群名推送消息就會(huì)帶上盐碱。
5.3多APP支持
對(duì)于需要多APP互通的場(chǎng)景把兔,可在多個(gè)APP中寫同一個(gè)sdkappid,可實(shí)現(xiàn)消息互通瓮顽,由于多個(gè)APP推送證書(shū)不同县好,所以需要在控制臺(tái)上提交多個(gè)證書(shū),每個(gè)證書(shū)在IM通訊云上生成一個(gè)編號(hào)暖混,可參考 3.1 Token 上報(bào) 設(shè)置證書(shū)聘惦,并提供當(dāng)前證書(shū)的編號(hào)。
6, 推送聲音
6.1 設(shè)置自己的推送聲音
不同用戶可能想使用不通的推送聲音儒恋,sdk提供了設(shè)置用戶聲音的接口善绎,可實(shí)現(xiàn)單聊聲音、群組聲音诫尽、音視頻(暫不支持)聲音的設(shè)置禀酱,也可在用戶級(jí)別設(shè)置是否接收推送。
* APNs 配置
*/
@interface TIMAPNSConfig : NSObject
/**
* 是否開(kāi)啟推送:0-不進(jìn)行設(shè)置 1-開(kāi)啟推送 2-關(guān)閉推送
*/
@property(nonatomic,assign) uint32_t openPush;
/**
* C2C消息聲音,不設(shè)置傳入nil
*/
@property(nonatomic,retain) NSString * c2cSound;
/**
* Group消息聲音,不設(shè)置傳入nil
*/
@property(nonatomic,retain) NSString * groupSound;
/**
* Video聲音,不設(shè)置傳入nil
*/
@property(nonatomic,retain) NSString * videoSound;
@end
@interface TIMManager : NSObject
/**
* 設(shè)置APNS配置
*
* @param config APNS配置
* @param succ 成功回調(diào)
* @param fail 失敗回調(diào)
*
* @return 0 成功
*/
-(int) setAPNS:(TIMAPNSConfig*)config succ:(TIMSucc)succ fail:(TIMFail)fail;
@end
參數(shù)說(shuō)明:
參數(shù) 說(shuō)明
config 設(shè)置配置
openPush : 是否開(kāi)啟推送 0-不進(jìn)行設(shè)置 1-開(kāi)啟推送 2-關(guān)閉推送
c2cSound : 單聊聲音牧嫉,文件名
groupSound : 群組聲音剂跟,文件名
videoSound : 音視頻邀請(qǐng)聲音,文件名
succ 成功回調(diào)
fail 失敗回調(diào)
6.2 獲取自己的推送聲音
界面展示如果需要獲取推送聲音酣藻,可使用 getAPNSConfig 獲取曹洽,此接口每次都從服務(wù)器同步數(shù)據(jù),不會(huì)進(jìn)行本地緩存辽剧。
@interface TIMManager : NSObject
/**
* 獲取APNS配置
*
* @param succ 成功回調(diào)送淆,返回配置信息
* @param fail 失敗回調(diào)
*
* @return 0 成功
*/
-(int) getAPNSConfig:(TIMAPNSConfigSucc)succ fail:(TIMFail)fail;
@end
參數(shù)說(shuō)明:
參數(shù) 說(shuō)明
succ 成功回調(diào),返回 TIMAPNSConfig 結(jié)構(gòu)體
fail 失敗回調(diào)
6.3 每條離線推送屬性
如果需要定制每條消息的展示文本怕轿、擴(kuò)展字段偷崩、提示音、是否推送屬性撞羽,可以在消息設(shè)置TIMOfflinePushInfo阐斜,此條消息在推送時(shí),會(huì)替換用戶原有的默認(rèn)屬性诀紊≮顺觯可實(shí)現(xiàn)每條消息定制化推送。填入kIOSOfflinePushNoSound到sound屬性時(shí)接收端強(qiáng)制為靜音提示
/**
填入sound字段表示接收時(shí)不會(huì)播放聲音
*/
extern NSString * const kIOSOfflinePushNoSound;
@interface TIMAndroidOfflinePushConfig : NSObject
/**
* 離線推送時(shí)展示標(biāo)簽
*/
@property(nonatomic,retain) NSString * title;
/**
* Android離線Push時(shí)聲音字段信息
*/
@property(nonatomic,retain) NSString * sound;
/**
* 離線推送時(shí)通知形式
*/
@property(nonatomic,assign) TIMAndroidOfflinePushNotifyMode notifyMode;
@end
@interface TIMIOSOfflinePushConfig : NSObject
/**
* 離線Push時(shí)聲音字段信息
*/
@property(nonatomic,retain) NSString * sound;
/**
* 忽略badge計(jì)數(shù)
*/
@property(nonatomic,assign) BOOL ignoreBadge;
@end
@interface TIMOfflinePushInfo : NSObject
/**
* 自定義消息描述信息狼牺,做離線Push時(shí)文本展示
*/
@property(nonatomic,retain) NSString * desc;
/**
* 離線Push時(shí)擴(kuò)展字段信息
*/
@property(nonatomic,retain) NSString * ext;
/**
* 推送規(guī)則標(biāo)志
*/
@property(nonatomic,assign) TIMOfflinePushFlag pushFlag;
/**
* iOS離線推送配置
*/
@property(nonatomic,retain) TIMIOSOfflinePushConfig * iosConfig;
/**
Android離線推送配置
*/
@property(nonatomic,retain) TIMAndroidOfflinePushConfig * androidConfig;
@end
上面的配置一定要在每條發(fā)送的message中配置一下断盛,要不然是不會(huì)接收到推送消息的。(就是因?yàn)楣δ苁窍茸龅慕穸梗扑褪呛蠹拥睦虿猓鞍姹镜膕dk相關(guān)的字段廢棄了颜骤,所以沒(méi)有配置,卡在了這里很多時(shí)間捣卤,確實(shí)需要檢討一下)
好了忍抽,差不多就是這么多了。本人的簡(jiǎn)書(shū)博客大部分都是項(xiàng)目中的遇到的自己遇到的一些問(wèn)題董朝,或者是自己無(wú)聊寫的一些小東西鸠项,沒(méi)有什么特別厲害的東西。把博客當(dāng)做是日記一樣寫出來(lái)子姜,主要是為了提高一下自己語(yǔ)言組織能力和問(wèn)題記錄的目的祟绊。。哥捕。牧抽。
大家加油!RWQ锸妗!