最近項(xiàng)目中用到了即時(shí)通訊,需要使用單聊 群聊等功能,經(jīng)過一番對(duì)比,最終選擇了阿里系的阿里百川IM,選擇阿里的原因主要就是免費(fèi)切后續(xù)沒有別的附加費(fèi)用和限制,所以就決定使用百川IM.
前段時(shí)間一直在忙著開發(fā)項(xiàng)目,在集成百川IM的過程中也遇到了不少的坑,所以想著項(xiàng)目做完閑下來寫篇文章來記錄一下,讓更多的攻城獅在以后的集成過程中少踩些坑.

下面我詳細(xì)介紹下阿里百川集成過程及其中的坑.
1. 準(zhǔn)備工作
- 阿里百川有詳細(xì)的前后端集成文檔,在接入之前強(qiáng)烈建議多讀幾遍
- 下載官方DEMO,運(yùn)行demo看下功能是否滿足項(xiàng)目需求.
DEMO下載地址
2. 百川創(chuàng)建應(yīng)用
- 創(chuàng)建時(shí)候有一個(gè)坑需要注意,因?yàn)榘沧縤OS聊天是需要同一個(gè)KEY,所以創(chuàng)建應(yīng)用的時(shí)候創(chuàng)建一個(gè)即可,因?yàn)閯?chuàng)建時(shí)候可以根據(jù)iOS的bundleID或安卓的包名,這點(diǎn)需要注意.
- 創(chuàng)建完應(yīng)用后就有了key,然后需要在項(xiàng)目中換成自己的key.
95D0BE6B-8D2E-4B59-9E6A-8FEE5B3CA7F1.png - 安全圖片,百川驗(yàn)證應(yīng)用信息是通過安全圖片驗(yàn)證的,所以需要申請(qǐng)安全圖片,申請(qǐng)圖片的版本V4 V5需要根據(jù)自己當(dāng)前庫版本決定,現(xiàn)在最近版的庫使用的是V4,這里注意(坑).
另外,在替換成自己項(xiàng)目的安全圖片時(shí),切記圖片名稱保持yw_1222.jpg不要換,因?yàn)榘俅ㄗx取的是這個(gè)名字的圖片.
FE23E4AF-E381-407C-AD8E-F369FA095D7C.png - 如果需要推送消息,還需要集成推送功能(安卓自帶有,iOS需要自己配置)
集成推送需要上傳推送證書,然后在項(xiàng)目中修改成自己的證書名.
上傳推送證書,需要在產(chǎn)品后臺(tái)開通百川即時(shí)通訊(這是現(xiàn)在版本,以前版本是在應(yīng)用下面選項(xiàng)就可以上傳),注意,這里直接用生產(chǎn)證書,開發(fā)證書在這里上傳無效,因?yàn)榘⒗锸褂玫纳a(chǎn)環(huán)境證書.
CDD0F3E5-BF06-4257-A573-A7A7BA3A6A4F.png
3. 項(xiàng)目集成過程
- 快速集成可參考百川文檔,根據(jù)文檔可快速集成IM(快速集成使用的是基本聊天功能,若需求有自定義等則需要自己修改開發(fā))
快速集成文檔 - 需要在 didFinishLaunchingWithOptions 初始化IM,這里也可以進(jìn)行登錄操作,同時(shí)在退出登錄時(shí),需要退出IM,不然切換賬號(hào)IM聊天信息還是上一個(gè)賬號(hào)的緩存.
// YWSDK快速接入接口,程序啟動(dòng)后調(diào)用這個(gè)接口
SPKitExample.sharedInstance().callThisInDidFinishLaunching()
if BaseUser.shareUser().isLogined{
SPKitExample.sharedInstance().callThisAfterISVAccountLoginSuccess(withYWLoginId: BaseUser.shareUser().getUserId(), passWord: IMUserPwd, preloginedBlock: {
}, successBlock: {
self.showToast(text: "IM登錄成功")
}, failedBlock: { (error) in
self.showToast(text: "IM登錄失敗")
})
}
退出登錄,同時(shí)退出百川IM.
SPKitExample.sharedInstance().callThisBeforeISVAccountLogout()
- 調(diào)單聊頁面
let person = YWPerson.init(personId: "對(duì)方百川id")
if SPKitExample.sharedInstance().ywIMKit == nil{self.showToast(text: "IM未登錄成功"); return} //IM未登錄成功
let conversation = YWP2PConversation.fetch(by: person, creatIfNotExist: true, baseContext: SPKitExample.sharedInstance().ywIMKit.imCore) //獲取單聊會(huì)話
vc_Chat = SPKitExample.sharedInstance().exampleMakeConversationViewController(with: conversation)// 創(chuàng)建會(huì)話Controller
SPKitExample.sharedInstance().ywIMKit.imCore.getContactService().enableContactOnlineStatus = true //顯示用戶是否在線 默認(rèn)為NO既默認(rèn)都顯示在線
vc_Chat?.viewDidLoadBlock = { [weak self] in
guard let strongSelf = self else{return}
//在這里可以進(jìn)行自定義 比如導(dǎo)航欄按鈕 標(biāo)題 請(qǐng)求數(shù)據(jù)等
}
navigationController?.pushViewController(vc_Chat!, animated: true)
注: 這里有個(gè)坑.iOS最新的版本 viewDidLoadBlock 這些view相關(guān)事件調(diào)出,是readonly只讀的,如果需要使用,把readonly去掉即可,如下:
@property (nonatomic, copy) YWViewDidLoadBlock viewDidLoadBlock;
//@property (nonatomic, copy, readonly) YWViewWillAppearBlock viewWillAppearBlock;
@property (nonatomic, copy) YWViewWillAppearBlock viewWillAppearBlock;
@property (nonatomic, copy, readonly) YWViewDidAppearBlock viewDidAppearBlock;
@property (nonatomic, copy, readonly) YWViewWillDisappearBlock viewWillDisappearBlock;
@property (nonatomic, copy, readonly) YWViewDidDisappearBlock viewDidDisappearBlock;
@property (nonatomic, copy, readonly) YWViewControllerWillDeallocBlock viewControllerWillDeallocBlock;
- 調(diào)群聊頁面
SPKitExample.sharedInstance().ywIMKit.imCore.getTribeService().requestTribe(fromServer: id_SelectGroupChat) { [weak self](tribute, error) in
guard let strongSelf = self else{return}
if SPKitExample.sharedInstance().ywIMKit == nil{weakSelf.showToast(text: "IM未登錄成功"); return}
let conversation = YWTribeConversation.fetch(by: tribute, createIfNotExist: true, baseContext: SPKitExample.sharedInstance().ywIMKit.imCore)
weakSelf.vc_Chat = SPKitExample.sharedInstance().exampleMakeConversationViewController(with: conversation)
weakSelf.vc_Chat.viewDidLoadBlock = { [weak self] in
guard let strongSelf = self else{return}
//這里是個(gè)例子,比如需求和微信類似,點(diǎn)右側(cè)導(dǎo)航進(jìn)群聊信息,則可這樣自定義,進(jìn)入自己的頁面進(jìn)行查看群信息和修改.
let btn = UIButton.init(frame: CGRect.init(x: 0, y: 0, width: 30, height: 40))
btn.setImage(UIImage.init(named: "群聊人員"), for: .normal)
btn.addTarget(strongSelf, action: #selector(strongSelf.rightNavBtnClick), for: .touchUpInside)
strongSelf.vc_Chat?.navigationItem.rightBarButtonItem = UIBarButtonItem.init(customView: btn)
}
weakSelf.vc_Chat.viewWillAppearBlock = { [weak self] (animated: Bool) in
//在這里可以進(jìn)行請(qǐng)求數(shù)據(jù)刷新導(dǎo)航等問題
guard let strongSelf = self else{return}
strongSelf.requestGroupPersonMessage(isRefreshData: true)
}
strongSelf.navigationController?.pushViewController(weakSelf.vc_Chat, animated: true)
}
注: 如果需要自定義,則需要將聊天控制器作為當(dāng)前類的屬性,既上面的vc_Chat,這樣可以在聊天頁面進(jìn)行push等操作,否則push等操作會(huì)在聊天前的頁面.
- 會(huì)話列表,因?yàn)榱奶靸?nèi)容涉及圖片 表情等,自己做起來復(fù)雜,可以直接使用百川的會(huì)話列表.
vc_ChatList = SPKitExample.sharedInstance().exampleMakeConversationListController { [weak self](conversation) in //點(diǎn)擊會(huì)話列表對(duì)應(yīng)會(huì)話.可跳轉(zhuǎn)對(duì)應(yīng)的聊天頁面
guard let strongSelf = self else{return}
//根據(jù)會(huì)話類可判斷選中的會(huì)話是單聊還是群聊
if conversation?.conversationType == .P2P{
strongSelf.type_IsGroup = false
}else{
strongSelf.type_IsGroup = true
}
strongSelf.vc_Chat = SPKitExample.sharedInstance().exampleMakeConversationViewController(with: conversation)
SPKitExample.sharedInstance().ywIMKit.imCore.getContactService().enableContactOnlineStatus = true //顯示用戶是否在線 默認(rèn)為NO既默認(rèn)都顯示在線
strongSelf.vc_Chat.viewDidLoadBlock = {
//自定義事件
}
strongSelf.navigationController?.pushViewController(weakSelf.vc_Chat, animated: true)
}
4. 自定義操作
參考文檔中心的快速集成和上述的過程,一個(gè)單聊 群聊等功能就做出來了,但有時(shí)候會(huì)遇到產(chǎn)品的一些自定義需求,比如打開頁面發(fā)一個(gè)商品信息等需求,這時(shí)候就需要自定義一個(gè)消息了.
這個(gè)我已發(fā)商品信息消息詳細(xì)說下,因?yàn)槲以谧龅臅r(shí)候查找了很多資料,發(fā)現(xiàn)沒有這方面的,就自己一個(gè)個(gè)坑踩了出來,實(shí)現(xiàn)了需求,我相信介紹下給大家,希望大家遇到相關(guān)需求可以輕松實(shí)現(xiàn).
-
首先,大家在集成的時(shí)候除非自己強(qiáng)迫癥或者需要重寫膠水代碼,否則使用DEMO中的膠水代碼即可, 里面有具體的初始化,登錄,自定義等操作,直接使用即可,而自定義消息就在膠水代碼中.
5632A2B3-D836-4C86-9826-B77EAF2AC076.png
如上圖所示,打碼的是我自定義的消息,上面兩個(gè)就是系統(tǒng)自帶的自定義消息是空的,CallingCard和Greeting是系統(tǒng)自定義的打招呼和發(fā)名片消息,在自定義的時(shí)候可以參考.
自定義消息ViewModel
#import <WXOUIModule/YWUIFMWK.h>
#import <WXOpenIMSDKFMWK/YWFMWK.h>
@interface 類名 : YWBaseBubbleViewModel
- (instancetype)initWithMessage:(id<IYWMessage>)aMessage;
/// 自定義消息體
@property (nonatomic, strong, readonly) YWMessageBodyCustomize *bodyCustomize;
@end
#import "TSBuyOrSaleChatModel.h"
@interface TSBuyOrSaleChatModel ()
/// 自定義消息體
@property (nonatomic, strong, readwrite) YWMessageBodyCustomize *bodyCustomize;
@end
@implementation TSBuyOrSaleChatModel
- (instancetype)initWithMessage:(id<IYWMessage>)aMessage
{
self = [super init];
if (self) {
/// 初始化
/// 記錄消息體,您也可以不記錄原始消息體,而是將數(shù)據(jù)解析后,記錄解析后的數(shù)據(jù)
self.bodyCustomize = (YWMessageBodyCustomize *)[aMessage messageBody];
/// 設(shè)置氣泡類型
// self.bubbleStyle = [aMessage outgoing] ? BubbleStyleCommonRight : BubbleStyleCommonLeft;
self.bubbleStyle = BubbleStyleNone; //修改為不顯示氣泡, 一般發(fā)送商品信息不需要?dú)馀莺皖^像,這里設(shè)置為none,則沒有氣泡和頭像
}
return self;
}
#pragma mark -
@end
- 自定義消息view
#import <WXOUIModule/YWUIFMWK.h>
@interface 類名 : YWBaseBubbleChatView
@end
#import "TSBuyOrSaleChatViewCustomize.h"
#import "TSBuyOrSaleChatModel.h"
#import "UIImageView+WebCache.h"
#import "Masonry.h"
@interface TSBuyOrSaleChatViewCustomize ()
@property (nonatomic, strong) UILabel*label;
/// 對(duì)應(yīng)的ViewModel
@property (nonatomic, strong, readonly) TSBuyOrSaleChatModel *viewModel;
@end
@implementation TSBuyOrSaleChatViewCustomize
@dynamic viewModel;
- (id)init
{
self = [super init];
if (self) {
/// 初始化
//在這里進(jìn)行初始化頁面操作
}
return self;
}
/// 計(jì)算尺寸,更新顯示
- (CGSize)_calculateAndUpdateFitSize
{
//這里可以解析上面自定義的ViewModel傳遞的content內(nèi)容,一般都是JSON轉(zhuǎn)為的字符串,這里可以轉(zhuǎn)為字典進(jìn)行使用
NSString *content = self.viewModel.bodyCustomize.content;
NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding];
NSError *err;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&err];
if(err){
NSLog(@"json解析失?。?@",err);
}
/// 你可以在這里根據(jù)消息的內(nèi)容,更新子view的顯示
// [self.label setText:self.viewModel.bodyCustomize.content];
//
//
//
// /// 加大內(nèi)容的邊緣,您可以根據(jù)您的卡片,調(diào)整合適的大小
// CGSize result = [self.label.text sizeWithFont:self.label.font constrainedToSize:CGSizeMake(200, 10000)];
// result.width += 20;
// result.height += 20;
//
// CGRect frame = self.frame;
// frame.size = result;
//
// /// 更新frame
// [self setFrame:frame];
//
// frame.size.height += 2;
// [self.label setFrame:frame];
// [self.label setCenter:CGPointMake(frame.size.width/2, frame.size.height/2)];
//
// return result;
}
#pragma mark - YWBaseBubbleChatViewInf
/// 這幾個(gè)函數(shù)是必須實(shí)現(xiàn)的
/// 內(nèi)容區(qū)域大小
- (CGSize)getBubbleContentSize
{
return [self _calculateAndUpdateFitSize];
}
/// 需要刷新BubbleView時(shí)會(huì)被調(diào)用
- (void)updateBubbleView
{
[self _calculateAndUpdateFitSize];
}
// 返回所持ViewModel類名,用于類型檢測
- (NSString *)viewModelClassName
{
return NSStringFromClass([對(duì)應(yīng)的viewmodel類名 class]);
}
@end
注意: 在m文件中要引入自定義的ViewModel.
- 發(fā)送自定義消息
/// 構(gòu)建一個(gè)自定義消息
var contentDictionary: [String: Any]
contentDictionary = dict //自己的json數(shù)據(jù)
contentDictionary["customizeMessageType"] = "CustomerChatMessage" //注 這個(gè)是必須有的,用于區(qū)分不同的自定義消息,key customizeMessageType是膠水代碼里固定的,直接使用即可
//將數(shù)據(jù)轉(zhuǎn)為字符串 通過消息體發(fā)送
let data = try? JSONSerialization.data(withJSONObject: contentDictionary, options: [])
let content = String.init(data: data!, encoding: String.Encoding.utf8)
let body = YWMessageBodyCustomize.init(messageCustomizeContent: content, summary: str_Msg)
/// 發(fā)送該自定義消息
vc_Chat.conversation.asyncSend(body, progress: { (progres, str) in
}) { (error, messageID) in
}
- 自定義消息
在創(chuàng)建聊天頁面,使用的膠水代碼exampleMakeConversationViewControllerWithConversation方法中有自定義消息.
/// 添加自定義表情
[self exampleShowCustomEmotionWithConversationController:conversationController];
根據(jù)自定義字段返回自己的ViewModel.
NSString *messageType = contentDictionary[kSPCustomizeMessageType];
if ([messageType isEqualToString:@"CallingCard"]) {
SPCallingCardBubbleViewModel *viewModel = [[SPCallingCardBubbleViewModel alloc] initWithMessage:message];
return viewModel;
}
else if ([messageType isEqualToString:@"Greeting"]) {
SPGreetingBubbleViewModel *viewModel = [[SPGreetingBubbleViewModel alloc] initWithMessage:message];
return viewModel;
}else if ([messageType isEqualToString:@"CustomerChatMessage"]){
TSBuyOrSaleChatModel *viewModel = [[TSBuyOrSaleChatModel alloc] initWithMessage:message]; //這里的字段為發(fā)消息時(shí)候自定義的字段, ViewModel為自定義的消息Model.
return viewModel;
}else{
SPBubbleViewModelCustomize *viewModel = [[SPBubbleViewModelCustomize alloc] initWithMessage:message];
return viewModel;
}
根據(jù)ViewModel使用對(duì)應(yīng)的自定義消息View
/// ChatView一般從ViewModel中獲取已經(jīng)解析的數(shù)據(jù),用于顯示
[aConversationController setHook4BubbleView:^YWBaseBubbleChatView *(YWBaseBubbleViewModel *viewModel) {
#if HAS_PRIVATEIMAGE
{
YWBaseBubbleChatView *cv = [[SPLogicBizPrivateImage sharedInstance] handleShowModel:viewModel];
if (cv) {
return cv;
}
}
#endif
if ([viewModel isKindOfClass:[SPCallingCardBubbleViewModel class]]) {
SPCallingCardBubbleChatView *chatView = [[SPCallingCardBubbleChatView alloc] init];
return chatView;
}
else if ([viewModel isKindOfClass:[SPGreetingBubbleViewModel class]]) {
SPGreetingBubbleChatView *chatView = [[SPGreetingBubbleChatView alloc] init];
return chatView;
}
else if ([viewModel isKindOfClass:[SPBubbleViewModelCustomize class]]) {
SPBaseBubbleChatViewCustomize *chatView = [[SPBaseBubbleChatViewCustomize alloc] init];
return chatView;
}
else if ([viewModel isKindOfClass:[TSBuyOrSaleChatModel class]]) {
TSBuyOrSaleChatViewCustomize *chatView = [[TSBuyOrSaleChatViewCustomize alloc] init];
return chatView; //這個(gè)為自定義view
}
return nil;
}];
注: 這里使用的是創(chuàng)建會(huì)話的膠水代碼,所以為了保證消息在不同的頁面都可以看到,要保證創(chuàng)建會(huì)話都用的這個(gè)膠水代碼,既可根據(jù)會(huì)話創(chuàng)建,使用我上面代碼中的創(chuàng)建方法即可.
其它自定義需求,比如導(dǎo)航顏色,消息顏色,默認(rèn)圖片等可參考文檔中自定義.

5.總結(jié)
寫到這里,相信對(duì)于百川即時(shí)通訊已經(jīng)滿足大部分人的需求了,單聊,群聊,會(huì)話列表,自定義等操作都可以實(shí)現(xiàn).我在集成過程中遇到了一些坑在上面有的我已經(jīng)在對(duì)于過程中一一說明出來了,有遇到對(duì)應(yīng)過程問題的,可以再次看下上面過程中注的坑和代碼中的注意地方,這些都很重要,是我自己根據(jù)自己經(jīng)驗(yàn)總結(jié)的,希望對(duì)大家有所幫助.
想看項(xiàng)目的話,可到github: 阿里百川IM, 對(duì)您有所幫助的話,star下哈.
最后給大家推薦一個(gè)論壇,里面有大部分集成的問題,可以在里面找到對(duì)應(yīng)的方法,官方的里面沒幾個(gè)人回答,比較坑...
集成問題解決方案匯總,大部分問題都能在該處找到解決方案. 另在集成過程中有別的問題可以給我評(píng)論和留言,我看到后會(huì)第一時(shí)間回復(fù). 如果對(duì)你有所幫助,喜歡的話,不妨給我一個(gè)小小的喜歡和關(guān)注哈!



