之前舊項目里的聊天是集成的融云,然而有諸多不方便在张,很多需求都無法正常且簡單的實現(xiàn)用含。而且使用聊天的人也并不多。帮匾。啄骇。
所以決定公司自己開發(fā)即時通訊(反正用戶少=。=)
開發(fā)中是基于Socket.IO封裝使用瘟斜,寫這篇文章時版本是13.0.0缸夹。后臺是Node.js。這是Github地址Socket.IO-Client-Swift
切記螺句!好的產(chǎn)品就別指望了虽惭!多和后臺的大兄弟交流,一定得有討論連接蛇尚,用戶認(rèn)證和消息文本格式的思維芽唇。
先上一個簡單的思維導(dǎo)圖
思維導(dǎo)圖.png
自己補(bǔ)的,大概就是這么個流程取劫。
下面開始上代碼4殷浴(去除掉需求邏輯)
//連接狀態(tài)enum linkState: String {caseno_connection ="未連接"casein_connection ="連接中"caseconnected ="已連接"caseconnection_error ="連接錯誤"}classBSIM{/// 靜態(tài)變量(常量)? static 修飾的靜態(tài)方法不能被重寫staticlet shared = BSIM()varmanager:SocketManager?varsocket:SocketIOClient?varonlineTimer:Timer?varonlineTimerNum =30;/*后臺狀態(tài)保活*/let app = UIApplication.sharedvartaskID = UIBackgroundTaskIdentifier()varbackTimer:Timer?varbackTime =0;//APP進(jìn)入后臺逼仔埃活時間varbackTimeOut =60*3;// MARK:- 初始連接并進(jìn)行認(rèn)證func initAndConnect(server:String,userid:String,random:String,result:@escaping BSIMConnectResult){/// 一些判斷 例如guard server !=""else{self.connectResult!(-9999,"服務(wù)器地址不能為空")return}/// 連接? 第一步的HTTP請求? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /// 打印調(diào)試信息? ? 使用websocketsself.manager = SocketManager(socketURL: URL(string:self.server)!, config: [.log(false), .forceWebsockets(true)])self.socket =self.manager?.defaultSocket/// 監(jiān)聽事件self.socket?.on("你和后臺約定好的字段", callback: { (data, ack) in/// 記得移除? 避免重復(fù)監(jiān)聽self.socket?.off("某些監(jiān)聽")/// 初始化定時器 心跳包if(self.onlineTimer == nil){self.onlineTimer = Timer.scheduledTimer(timeInterval: TimeInterval(self.onlineTimerNum),target:self,selector:#selector(self.onlineTimerRun),userInfo:nil,repeats:true)}else{self.onlineTimer?.invalidate()self.onlineTimer = nilself.onlineTimer = Timer.scheduledTimer(timeInterval: TimeInterval(self.onlineTimerNum),target:self,selector:#selector(self.onlineTimerRun),userInfo:nil,repeats:true)}/// 更改連接狀態(tài)self.linkStatePush(state: linkState.connected)/*
? ? ? ? ? 有關(guān)認(rèn)證的邏輯代碼
? ? ? ? ? *//// 基本設(shè)置self.completion()? ? ? ? })? ? ? ? }/// 初始化設(shè)置privatefunc completion(){/// 移除自身通知NotificationCenter.default.removeObserver(self)//注冊進(jìn)入后臺通知NotificationCenter.default.addObserver(self, selector:#selector(self.EnterBackgroundNotification), name:NSNotification.Name.UIApplicationDidEnterBackground, object: nil)//注冊進(jìn)入前臺通知NotificationCenter.default.addObserver(self, selector:#selector(self.EnterForegroundNotification), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)self.onMessage(MsgAction:"message") { (dataString) in? ? ? ? ? ? ? ? ? ? ? ? let data = dataString.data(using: String.Encoding.utf8)? ? ? ? ? ? let js = JSON(data:data!)/// 自定義的Modellet msgModel = MsgBaseModel.init()/// 收到數(shù)據(jù)ifjs["msgId"].stringValue !=""{/*
? ? ? ? ? ? ? ? 解析數(shù)據(jù)
? ? ? ? ? ? ? ? 數(shù)據(jù)解析用的SwiftyJSON
? ? ? ? ? ? ? ? 本地數(shù)據(jù)存儲用的FMDB
? ? ? ? ? ? ? ? *///聲音提醒self.applicationState(pushData: ((js["sendName"].stringValue) ==""? (js["pushData"].stringValue) : (js["sendName"].stringValue) +": "+ (js["pushData"].stringValue)))if(self.checkedMessage(MsgId: msgModel.msgId)){print("\(msgModel.msgId) 消息數(shù)據(jù)庫中已存在")return}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? let ins =self.insertMsg(model: msgModel)if(ins ==false){print("\(msgModel.msgId) 消息插入失敗")return}/// 通知 更新UI界面self.SessionListNotice?.newMessage(MsgModel: msgModel)self.SessionNotice?.newMessage(MsgModel: msgModel)/// 未讀消息條數(shù)let arr =self.getSessionListData()varnumber =0formodel in arr{? ? ? ? ? ? ? ? ? ? number = number + model.notReadNumber? ? ? ? ? ? ? ? ? ? let na = NSNotification.Name(rawValue:"news")? ? ? ? ? ? ? ? ? ? NotificationCenter.default.post(name: na, object:(number))? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }? ? ? ? ? ? }/// 監(jiān)聽消息privatefunc onMessage(MsgAction:String,cb:@escaping (_ data:String)->Void){self.socket?.on(MsgAction, callback: { (data, ack) in/// 在TCP/IP協(xié)議中炮捧,如果接收方成功的接收到數(shù)據(jù),那么會回復(fù)一個ACK數(shù)據(jù)ack.with(UUID().uuidString)ifdata[0] is String{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cb(data[0]as? String ??"")? ? ? ? ? ? ? ? ? ? ? ? ? ? }else{//此處后期可能語音圖片等格式文件需要做相應(yīng)判斷cb(bs_String.objectToJson(object: (data[0]as? Dictionary) ?? [:]) ??"")? ? ? ? ? ? }? ? ? ? })? ? }? ? ? ? @objc func EnterForegroundNotification(){print("進(jìn)入前臺")if(self.backTime {/// 邏輯代碼}? ? ? ? })? ? } }
以上代碼差不多是連接惦银,設(shè)置(心跳包咆课,通知等)末誓,收到消息(處理數(shù)據(jù),更新UI)等一系列方法的去邏輯版傀蚌。本地數(shù)據(jù)存儲也就是檢查下有沒有重復(fù)基显,不存在就插入。
這樣就基本實現(xiàn)了用戶登錄善炫,可以接收到產(chǎn)品想要的自定義消息撩幽,比如系統(tǒng)消息,賬單消息等等箩艺。但是如果需要單聊窜醉,你就需要根據(jù)用戶id取聊天數(shù)據(jù),然后條數(shù)多了肯定需要分頁艺谆。所以要多一個getUserMsgListPageData(userid:String, page:Int)這種方法榨惰。
然后消息主頁,子界面的那些處理就需要自己去慢慢拓展了静汤。