阿里百川即時(shí)通訊IM集成

最近項(xiàng)目中用到了即時(shí)通訊,需要使用單聊 群聊等功能,經(jīng)過一番對(duì)比,最終選擇了阿里系的阿里百川IM,選擇阿里的原因主要就是免費(fèi)切后續(xù)沒有別的附加費(fèi)用和限制,所以就決定使用百川IM.

前段時(shí)間一直在忙著開發(fā)項(xiàng)目,在集成百川IM的過程中也遇到了不少的坑,所以想著項(xiàng)目做完閑下來寫篇文章來記錄一下,讓更多的攻城獅在以后的集成過程中少踩些坑.

664070306cb6ce2e8e0e297f5f086aca.jpg
下面我詳細(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)圖片等可參考文檔中自定義.

61122FD7-330A-4713-8398-9E2237B9A140.png

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)注哈!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容