美文网首页
iOS社交项目(使用环信二次开发)

iOS社交项目(使用环信二次开发)

作者: 约翰丶码农 | 来源:发表于2020-02-11 17:12 被阅读0次

    前言

    时光如梭,2019转瞬即逝。去年手头上做了公司的两个新项目,一个教育平台类一个社交类,任务有点重但都挺过来了。社交类项目公司原计划春节后测试验收完毕开始投放市场,但今年伊始一场肺炎席卷了整个国家,也打乱了公司的部署。目前公司未安排线上办公,上班也是遥遥无期,看着房贷看着银行存款让我鸭梨山大。今天是2020年2月11日,啰嗦半天,闲来无事记录下社交项目中趟的坑。

    集成环信

    • cocoapods管理环信SDK


      截屏2020-02-11下午2.51.11.png

    我的项目中主要使用了环信的基本IM还有实时语音功能,但项目是用swift写的,所以自己写了一些供swift里使用的头文件,这里就不做赘述了。

    • 项目中调用
      环信官方demo代码写的很详细,可直接给代码文件copy进自己的项目中,使用很简单。
    截屏2020-02-11下午3.17.08.png

    图1,项目使用了上图中环信demo里的文件夹文件(IM基类是自己写的)

    1.环信管理
    以下方法都是封装在IM管理类中,方便调用

    class TTIMClass: NSObject {
        
        public static let shared = TTIMClass.init()
        private override init() {}
        
        var isSystem:Bool = false // 是否为系统消息
        
        // 接收新消息回调
        typealias didReceiveBlock = (_ messages:[Any]) ->Void
        var didReceiveCallBack:didReceiveBlock?
        
        // 接收新CMD透传消息回调
        typealias didCMDReceiveBlock = (_ messages:[Any]) ->Void
        var didMDReceiveCallBack:didCMDReceiveBlock?
        
        // 接收会话消息列表变化回调
        typealias conversationListBlock = (_ messages:[Any]) ->Void
        var conversationListCallBack:conversationListBlock?
        
        // 自动登录成功与否
        typealias autoLoginBlock  = () ->Void
        var autoLoginCallBack:autoLoginBlock?
    
        
        /// 初始化环信
         func initIM(){
            
            let options = EMOptions.init(appkey: EASEMOB_KEY)
            options?.apnsCertName = "推送开发证书"
            EMClient.shared()?.initializeSDK(with: options)
            
            // 账号
            EMClient.shared()?.add(self, delegateQueue: nil)
            
            // 接收消息
            EMClient.shared()?.chatManager.add(self, delegateQueue: nil)
            
            // 实时语音
            TTChatCallManager.shared()
        }
        
        
        /// 登录环信
        /// - Parameters:
        ///   - username: username
        ///   - pwd: pwd
        ///   - resultCallBack: 登录回调 resultCallBack
        func setIMLogin(username:String, pwd:String, resultCallBack: @escaping (_ result:String?, _ emErr:EMError?)->Void){
            
            // 判断是否设置了自动登录
            let isAutoLogin:Bool =  EMClient.shared()?.options.isAutoLogin ?? false
            if !isAutoLogin {
                
                EMClient.shared()?.login(withUsername: username, password: pwd, completion: resultCallBack)
            }else{
                
               printLog(message: "未设置环信自动登录")
            }
            
        }
        
        
        /// 个人详情跳转聊天页
        /// - Parameters:
        ///   - receiveModel: 接收方mdoel
        ///   - sendModel: 发送方mdoel
        ///   - presentController: presentController
        func jumpChatViewController(receiveModel:TTPersonalModel, sendModel:TTPersonalModel, presentController:UIViewController!){
            
            let authIamges = ["", "真人icon", "女神"];
            
            let chatModel = TTChatModel.share()
            chatModel.member_id = receiveModel.member_id
            chatModel.easemob_id = receiveModel.easemob_id
            chatModel.memo_name = receiveModel.memo_name
            chatModel.memo_desc = receiveModel.memo_desc
            chatModel.nickname = receiveModel.nickname
            chatModel.gender = receiveModel.gender
            chatModel.avatar_url = receiveModel.avatar_url
            chatModel.sendAvatar_url = TTDefaultsData.setProfileModel().avatar_url
            chatModel.city = receiveModel.city
            chatModel.age = receiveModel.easemob_id
            chatModel.qq = receiveModel.qq
            chatModel.wechat = receiveModel.wechat
            chatModel.online_status = receiveModel.online_status
            chatModel.like_status = receiveModel.like_status
            chatModel.block_status = receiveModel.block_status
            chatModel.chat_status = receiveModel.chat_status
            chatModel.voice_status = receiveModel.voice_status
            
            chatModel.vip_level = receiveModel.vip_level
            chatModel.vip_name = receiveModel.vip_name
            chatModel.auth_status = receiveModel.auth_status
            chatModel.auth_name = receiveModel.auth_name
            chatModel.authImage = authIamges[Int(receiveModel.auth_status) ?? 0]
            chatModel.complaint_url = TTDefaultsData.setAppInfoModel().linkModel.complaint_url
            
            chatModel.send_member_id = sendModel.member_id
            chatModel.send_username  = TTDefaultsData.setProfileModel().username
            chatModel.send_nickname  = TTDefaultsData.setProfileModel().nickname
            chatModel.send_memoname  = sendModel.memo_name
            chatModel.send_voice_status = sendModel.voice_status
            chatModel.send_block_status = sendModel.block_status
    
            chatModel.send_avatarUrl = TTDefaultsData.setProfileModel().avatar_url
            chatModel.send_level = TTDefaultsData.setProfileModel().auth_status
            chatModel.send_authImage = authIamges[Int(TTDefaultsData.setProfileModel().auth_status) ?? 0]
    
            chatModel.account_name = TTDefaultsData.setProfileModel().nickname
            chatModel.account_avatar = TTDefaultsData.setProfileModel().avatar_url
            chatModel.account_auth_status = TTDefaultsData.setProfileModel().auth_status
            chatModel.account_member_id = TTDefaultsData.setProfileModel().user_id
    
            
            let chatVC = TTChatViewController.init(conversationId: receiveModel.easemob_id,
                                                   type: EMConversationTypeChat,
                                                   chatModel: chatModel,
                                                   createIfNotExist: true)
            presentController.navigationController!.pushViewController(chatVC, animated: true)
    
        }
        
        
        
        /// 消息中心跳转聊天页
        /// - Parameters:
        ///   - conversationModel: 消息实体
        ///   - receiveModel: 接收方mdoel
        ///   - sendModel: 发送方mdoel
        ///   - presentController: presentController
        func jumpMessageChatViewController(conversationModel:EMConversationModel, receiveModel:TTPersonalModel, sendModel:TTPersonalModel, presentController:UIViewController!){
            
            let authIamges = ["", "真人icon", "女神"];
            
            let chatModel = TTChatModel.share()
            chatModel.member_id = receiveModel.member_id
            chatModel.easemob_id = receiveModel.easemob_id
            chatModel.memo_name = receiveModel.memo_name
            chatModel.memo_desc = receiveModel.memo_desc
            chatModel.nickname = receiveModel.nickname
            chatModel.gender = receiveModel.gender
            chatModel.avatar_url = receiveModel.avatar_url
            chatModel.sendAvatar_url = TTDefaultsData.setProfileModel().avatar_url
            chatModel.city = receiveModel.city
            chatModel.age = receiveModel.easemob_id
            chatModel.qq = receiveModel.qq
            chatModel.wechat = receiveModel.wechat
            chatModel.online_status = receiveModel.online_status
            chatModel.like_status = receiveModel.like_status
            chatModel.block_status = receiveModel.block_status
            chatModel.chat_status = receiveModel.chat_status
            chatModel.voice_status = receiveModel.voice_status
    
            chatModel.vip_level = receiveModel.vip_level
            chatModel.vip_name = receiveModel.vip_name
            chatModel.auth_status = receiveModel.auth_status
            chatModel.auth_name = receiveModel.auth_name
            chatModel.authImage = authIamges[Int(receiveModel.auth_status) ?? 0]
            chatModel.complaint_url = TTDefaultsData.setAppInfoModel().linkModel.complaint_url
    
            chatModel.send_member_id = sendModel.member_id
            chatModel.send_username  = sendModel.username
            chatModel.send_nickname  = sendModel.nickname
            chatModel.send_memoname  = sendModel.memo_name
            chatModel.send_block_status = sendModel.block_status
            chatModel.send_voice_status = sendModel.voice_status
            chatModel.send_avatarUrl = sendModel.avatar_url
            chatModel.send_level = sendModel.auth_status
            chatModel.send_authImage = authIamges[Int(sendModel.auth_status) ?? 0]
    
            chatModel.account_name = TTDefaultsData.setProfileModel().nickname
            chatModel.account_avatar = TTDefaultsData.setProfileModel().avatar_url
            chatModel.account_auth_status = TTDefaultsData.setProfileModel().auth_status
            chatModel.account_member_id = TTDefaultsData.setProfileModel().user_id
            
            
            let chatVC = TTChatViewController.init(coversationModel: conversationModel, chatModel: chatModel)
            presentController.navigationController!.pushViewController(chatVC, animated: true)
    
        }
        
        
        /// 连麦
        /// - Parameter receiveModel: receiveModel 连麦对象
        func setLineMic(receiveModel:TTPersonalModel){
            
            let chatModel = TTChatModel.share()
            chatModel.member_id = receiveModel.member_id
            chatModel.easemob_id = receiveModel.easemob_id
            chatModel.memo_name = receiveModel.memo_name
            chatModel.memo_desc = receiveModel.memo_desc
            chatModel.nickname = receiveModel.nickname
            chatModel.gender = receiveModel.gender
            chatModel.avatar_url = receiveModel.avatar_url
            chatModel.sendAvatar_url = TTDefaultsData.setProfileModel().avatar_url
            chatModel.online_status = receiveModel.online_status
            chatModel.like_status = receiveModel.like_status
            chatModel.block_status = receiveModel.block_status
            chatModel.chat_status = receiveModel.chat_status
            
            chatModel.vip_level = receiveModel.vip_level
            chatModel.vip_name = receiveModel.vip_name
            chatModel.auth_status = receiveModel.auth_status
            chatModel.auth_name = receiveModel.auth_name
            
            chatModel.send_nickname = TTDefaultsData.setProfileModel().nickname
            chatModel.send_avatarUrl = TTDefaultsData.setProfileModel().avatar_url
            chatModel.send_level = TTDefaultsData.setProfileModel().auth_status
    
            
            
            let conversation = EMClient.shared()?.chatManager.getConversation(receiveModel.easemob_id, type: EMConversationTypeChat, createIfNotExist: true)
            let conversationModel = EMConversationModel.init(emModel: conversation!)
            
            NotificationCenter.post(name: .EMMake1v1Call,
                                    object: ["chatter":conversationModel.emModel.conversationId as Any, "type":NSNumber(value: EMCallTypeVoice.rawValue)],
                                    userInfo: nil)
        }
       
        
        /// 退出登录
        /// - Parameters:
        ///   - resultCallBack: 退出登录回调
        func setExitIM(resultCallBack: @escaping (_ emErr:EMError?)->Void){
            
            
            EMClient.shared()?.logout(true, completion: resultCallBack)
        }
        
        
        
        /// 获取未读信息角标数量
        func loadConversationBadge()->Int{
            
            var unreadCount:Int = 0
    
    //        if TT_USERID != "0" && EMClient.shared()?.isConnected ?? false{
                
                let conversations:Array<EMConversation> = EMClient.shared()?.chatManager.getAllConversations() as! Array<EMConversation>
                for conversation in conversations{
    
                    unreadCount += Int(conversation.unreadMessagesCount)
                }
    //        }
            
            return unreadCount
        }
        
        
        /// 消息未读角标
        func setUnreadMessagesCount(count: Int){
                
    //        if TT_USERID != "0" && EMClient.shared()?.isConnected ?? false{
                
                let appDelegate = UIApplication.shared.delegate as! AppDelegate
                let tttbc:TTTabBarController = appDelegate.window?.rootViewController as! TTTabBarController
                tttbc.setTabbarUnreadMessagesCount(self.loadConversationBadge() + count)
                EMRemindManager.updateApplicationIconBadgeNumber(self.loadConversationBadge() + count)
    //        }
        }
        
        
        
        /// 清空角标
        func setClearUnreadMessagesCount(){
                
            let appDelegate = UIApplication.shared.delegate as! AppDelegate
            let tttbc:TTTabBarController = appDelegate.window?.rootViewController as! TTTabBarController
            tttbc.setTabbarUnreadMessagesCount(0)
            EMRemindManager.updateApplicationIconBadgeNumber(0)
            
        }
        
    }
    
    //MARK: ***** 账号异常处理 *****
    extension TTIMClass:EMClientDelegate{
        
        // 自动登录返回结果
        func autoLoginDidCompleteWithError(_ aError: EMError!) {
            
            if aError != nil{
                
                var errorDes = "登录失败,请重试"
    
                switch (aError!.code) {
                    case EMErrorUserNotFound:
                        errorDes = "用户ID不存在"
                        break
                    case EMErrorNetworkUnavailable:
                        errorDes = "网络未连接"
                        break
                    case EMErrorServerNotReachable:
                        errorDes = "无法连接服务器"
                        break
                    case EMErrorUserAuthenticationFailed:
                        errorDes = aError!.errorDescription
                        break
                    case EMErrorUserLoginTooManyDevices:
                        errorDes = "登录设备数已达上限"
                        break
                    case EMErrorUserAlreadyLogin:
                        errorDes = "用户已登录"
                        break
                    case EMErrorUserLoginOnAnotherDevice:
                        errorDes = "当前用户在另一台设备上登录"
                        break
                    default:
                        break
                }
                
                printLog(message: "环信自动登录失败结果\(errorDes)  code\(aError!.code)")
                
                if !TT_SINGLE_ONCE.isOffline {
                    
                    TT_SINGLE_ONCE.isOffline = true
                    TTUserTool.exitLogin(isActive: false)
                    NotificationCenter.post(name: .EMPushCallViewController)
    
                    let aDelegate = UIApplication.shared.delegate as! AppDelegate
                    TTAlertController.setAlertController(presentController: aDelegate.window!.rootViewController!, title:nil , messgae: "\(errorDes)!", cancel: nil, define: "我知道了") { (define) in
                        
                        TTUserTool.pushLoginPageAndResetDefault()
                    }
                }
                
                
            }else{
                
                printLog(message: "IM自动成功")
                if autoLoginCallBack != nil{
                    
                    autoLoginCallBack!()
                }
            }
            
        }
        
        
        // 当前登录账号已经被从服务器端删除时会收到该回调
        func userAccountDidRemoveFromServer() {
            
            printLog(message: "当前登录账号已经被从服务器端删除时会收到该回调")
            
            if !TT_SINGLE_ONCE.isOffline {
                
                TT_SINGLE_ONCE.isOffline = true
                TTUserTool.exitLogin(isActive: false)
                NotificationCenter.post(name: .EMPushCallViewController)
    
                let aDelegate = UIApplication.shared.delegate as! AppDelegate
                TTAlertController.setAlertController(presentController: aDelegate.window!.rootViewController!, title:nil , messgae: "账号已经在其他设备上登录,该设备账号已下线!", cancel: nil, define: "我知道了") { (define) in
                    
                    TTUserTool.pushLoginPageAndResetDefault()
                }
            }
        }
        
        // 当前登录账号在其它设备登录时会接收到该回调
        func userAccountDidLoginFromOtherDevice() {
            
            printLog(message: "当前登录账号在其它设备登录时会接收到该回调")
    
            
            if !TT_SINGLE_ONCE.isOffline {
                
                TT_SINGLE_ONCE.isOffline = true
                TTUserTool.exitLogin(isActive: false)
                NotificationCenter.post(name: .EMPushCallViewController)
    
                let aDelegate = UIApplication.shared.delegate as! AppDelegate
                TTAlertController.setAlertController(presentController: aDelegate.window!.rootViewController!, title:nil , messgae: "账号已经在其他设备上登录,该设备账号已下线!", cancel: nil, define: "我知道了") { (define) in
                    
                    TTUserTool.pushLoginPageAndResetDefault()
                }
                
            }
            
        }
        
        
        
    }
     
    
    
    //MARK: ***** 接收消息 *****
    extension TTIMClass:EMChatManagerDelegate{
        
        // 解析普通消息
        func messagesDidReceive(_ aMessages: [Any]!) {
            
            isSystem = false
            let messageArray:Array<EMMessage> = aMessages as! Array<EMMessage>
            printLog(message: "接收普通消息\(aMessages ?? Array<Any>())")
            
            for messages:EMMessage in messageArray {
                
                if messages.ext != nil {
                               
                    let ext:Dictionary  = messages.ext
                    printLog(message: "接收普通扩展属性消息 \(ext) id\(String(describing: messages.conversationId)) to\(String(describing: messages.to)) from\(String(describing: messages.from))")
                    
                    if ext.keys.contains("type"){ // 系统消息,字典包含type为系统消息,走系统消息推送逻辑
                        
                        isSystem = true
                        printLog(message: "系统消息 \(ext)")
                        
                        let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 0.01, repeats: false)
                        let content = UNMutableNotificationContent.init()
                        content.sound = UNNotificationSound.default
                        content.title = ext.keys.contains("title") ? (ext["title"] as! String) : ""
                        content.body = ext.keys.contains("content") ? (ext["content"] as! String) : "你有一条新消息"
                        content.userInfo = ["f":"admin"] // value = admin为系统消息,其他为IM消息,详见AppDelegate 点击通知回调方法
                        let request = UNNotificationRequest.init(identifier: messages.conversationId, content: content, trigger: trigger)
                        UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
    
                        // 收到钱包通知,请求个人资金信息
                        if "\(ext["type"] ?? "")" == "4"{
                            
                            TTNetworkBackgroundRequest.requestAccountData()
                        }
                        
                        break
                    }
                }
                
                /* 本地通知IM消息
                   在长连接还存在的时候,通过环信收消息的回调接收到消息,之后判断当前App的状态,
                   如果是在后台的情况下,就可以`通过代码主动弹出一个通知`,来起到通知用户的作用
                */
                EMRemindManager.remind(messages)
            }
        
            if !isSystem{
                
                self.setUnreadMessagesCount(count: Defaults[.unread_system_count])
                NotificationCenter.post(name: .SYSTEM_MSG_UNREADNUM, object: ["message_un_read_num": "\(self.loadConversationBadge())"], userInfo: nil)
    
                if didReceiveCallBack != nil{
    
                    didReceiveCallBack!(aMessages)
                }
            }
            
            
        }
        
        // 会话列表发生变化
        func conversationListDidUpdate(_ aConversationList: [Any]!) {
            
            self.setUnreadMessagesCount(count: Defaults[.unread_system_count])
    
            if conversationListCallBack != nil{
                
                conversationListCallBack!(aConversationList)
            }
        }
        
        
        // 透传消息
        func cmdMessagesDidReceive(_ aCmdMessages: [Any]!) {
            
            if didMDReceiveCallBack != nil{
                
                didMDReceiveCallBack!(aCmdMessages)
            }
            
            let messageArray:Array<EMMessage> = aCmdMessages as! Array<EMMessage>
            printLog(message: "-----接收透传消息\(aCmdMessages ?? Array<Any>())")
    
            // 解析消息扩展属性
            for messages:EMMessage in messageArray {
                
                // cmd消息中的扩展属性
                if messages.ext != nil {
                    
                    let ext:Dictionary  = messages.ext
                    printLog(message: "接收透传扩展属性消息 \(ext)")
                }
            }
            
            
        }
        
        
        
    }
    
    1. TTIMClass里写的isSystem变量,在APP活跃状态下区分消息是即时通信还是服务器推送消息。
      TTIMClass页面跳转方法是根据自身项目的业务逻辑封装,里面所传model是为了在聊天页显示用户的头像、用户名和一些基本信息,因为环信的服务器不会记录集成方服务器的头像和用户名还有个人信息,所以给一个model来记录个人的一些信息,方便在聊天页和消息页使用。
      如在聊天页界面使用,必须在聊天的信息里添加扩展信息,用来在即时消息里传递用户的信息。

    如下图这样:
    图2

    243D9A3673A566477DCD1996E37D7D09.png

    图3

    E830C3BC1D3F347B170A0BE8F1E800EE.png

    代码如下:
    图2这里使用的是环信demo里的ChatViewController,自己做了修改

    /// 发送扩展消息
    @property (strong, nonatomic) NSMutableDictionary *sendExt;
    
     self.sendExt = [@{@"member_id":self.chatModel.send_member_id,             // 发送人member id
                       @"nick":self.chatModel.send_nickname,                    // 发送人昵称
                       @"avatar":self.chatModel.send_avatarUrl,                 // 发送人头像
                       @"level":self.chatModel.send_level,                      // 发送人认证状态
                       @"memoname":self.chatModel.send_memoname,                // 发送人备注名
                       @"blockstatus":self.chatModel.send_block_status,         // 发送人是否被拉黑
                       @"voice_status":self.chatModel.send_voice_status,        // 发送人是否允许连麦
    
                       @"recive_member_id":self.chatModel.member_id,            // 接收人member id
                       @"recive_nick":self.chatModel.nickname,                  // 接收人昵称
                       @"recive_avatar":self.chatModel.avatar_url,              // 接收人头像
                       @"recive_level":self.chatModel.auth_status,              // 接收人认证状态
                       @"recive_memoname":self.chatModel.memo_name,             // 接收人备注名
                       @"recive_block_status":self.chatModel.block_status,      // 接收人是否被拉黑
                       @"recive_voice_status":self.chatModel.voice_status,      // 接收人是否允许连麦
                       @"complaint_url":self.chatModel.complaint_url            // 举报URL
                      }mutableCopy];
    

    发送消息时使用

    #pragma mark - Send Message
    /// 文本消息
    - (void)_sendTextAction:(NSString *)aText ext:(NSDictionary *)aExt{
        
        [self.chatBar clearInputViewText];
        if ([aText length] == 0) {
            return;
        }
     
        //TODO: 处理表情
        //    NSString *sendText = [EaseConvertToCommonEmoticonsHelper convertToCommonEmoticons:aText];
            EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:aText];
        [self _sendMessageWithBody:body ext:aExt isUpload:NO];
        
        if (self.enableTyping) {
            [self _sendEndTyping];
        }
    }
    /// 图片消息
    - (void)_sendImageDataAction:(NSData *)aImageData{
       
        EMImageMessageBody *body = [[EMImageMessageBody alloc] initWithData:aImageData displayName:@"image"];
        [self _sendMessageWithBody:body ext:self.sendExt isUpload:YES];
    }
    /// 位置消息
    - (void)_sendLocationAction:(CLLocationCoordinate2D)aCoord address:(NSString *)aAddress{
        EMLocationMessageBody *body = [[EMLocationMessageBody alloc] initWithLatitude:aCoord.latitude longitude:aCoord.longitude address:aAddress];
        [self _sendMessageWithBody:body ext:self.sendExt isUpload:NO];
    }
    /// 语音消息
    - (void)_sendVideoAction:(NSURL *)aUrl{
        EMVideoMessageBody *body = [[EMVideoMessageBody alloc] initWithLocalPath:[aUrl path] displayName:@"video.mp4"];
        [self _sendMessageWithBody:body ext:self.sendExt isUpload:YES];
    }
    

    图3消息页面里点UITableView跳转进入聊天页,再把收到的消息里的扩展传给聊天页。因为用户在作为接收方的场景下,在收到新消息,只能用发送方那边消息的扩展,所以在跳转进入聊天页时需要把会话的用户信息传入。

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            
            let model:EMConversationModel = datas[indexPath.row] as! EMConversationModel
            let conversation:EMConversation = model.emModel
            let lastMessage:EMMessage = conversation.latestMessage
            let ext = lastMessage.ext
            let sendModel = TTPersonalModel()
            let receiveModel = TTPersonalModel()
            
            if ext != nil{
                // 发送方
                sendModel.nickname = ext != nil ? (ext?.keys.contains("nick") ?? false ? (ext!["nick"] as! String) : "") : ""
                sendModel.avatar_url = ext != nil ? (ext?.keys.contains("avatar") ?? false ? (ext!["avatar"] as! String) : "") : ""
                sendModel.memo_name = ext != nil ? (ext?.keys.contains("memoname") ?? false ? (ext!["memoname"] as! String) : "") : ""
                sendModel.member_id = ext != nil ? (ext?.keys.contains("member_id") ?? false ? (ext!["member_id"] as! String) : "") : ""
                sendModel.block_status = ext != nil ? (ext?.keys.contains("blockstatus") ?? false ? (ext!["blockstatus"] as! String) : "") : ""
                sendModel.auth_status = ext != nil ? (ext?.keys.contains("level") ?? false ? (ext!["level"] as! String) : "") : ""
                sendModel.voice_status = ext != nil ? (ext?.keys.contains("voice_status") ?? false ? (ext!["voice_status"] as! String) : "") : ""
    
                // 接收方
                receiveModel.easemob_id = conversation.conversationId
                receiveModel.member_id = ext != nil ? (ext?.keys.contains("recive_member_id") ?? false ? (ext!["recive_member_id"] as! String) : "") : ""
                receiveModel.nickname = ext != nil ? (ext?.keys.contains("recive_nick") ?? false ? (ext!["recive_nick"] as! String) : "") : ""
                receiveModel.avatar_url = ext != nil ? (ext?.keys.contains("recive_avatar") ?? false ? (ext!["recive_avatar"] as! String) : "") : ""
                receiveModel.memo_name = ext != nil ? (ext?.keys.contains("recive_memoname") ?? false ? (ext!["recive_memoname"] as! String) : "") : ""
                receiveModel.block_status = ext != nil ? (ext?.keys.contains("recive_block_status") ?? false ? (ext!["recive_block_status"] as! String) : "") : ""
                receiveModel.voice_status = ext != nil ? (ext?.keys.contains("voice_status") ?? false ? (ext!["voice_status"] as! String) : "") : ""
                receiveModel.auth_status = ext != nil ? (ext?.keys.contains("recive_level") ?? false ? (ext!["recive_level"] as! String) : "") : ""
                receiveModel.complaint_url = ext != nil ? (ext?.keys.contains("complaint_url") ?? false ? (ext!["complaint_url"] as! String) : "") : ""
            }
            
            TTIMClass.shared.jumpMessageChatViewController(conversationModel: model, receiveModel: receiveModel, sendModel: sendModel, presentController: self)
            
        }
    

    推送

    项目统一使用环信做推送,有IM消息推送、服务器根据用户个人自定义推送、系统推送,不同的推送业务逻辑处理也不一样,所以一定要区别对待。

    AppDelegate:

        /// 已经进入后台
        /// - Parameter application: application
        func applicationDidEnterBackground(_ application: UIApplication) {
                // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
                // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
        
            EMClient.shared()?.applicationDidEnterBackground(application)
        }
    
        
        /// 即将进入前台
        /// - Parameter application: application
        func applicationWillEnterForeground(_ application: UIApplication) {
                // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
            
            EMClient.shared()?.applicationWillEnterForeground(application)
        }
    
    //MARK: ***** 推送 *****
    extension AppDelegate:UNUserNotificationCenterDelegate{
        
        /// 注册远程通知
        func setRegisterRemoteNotification(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?){
               
            application.applicationIconBadgeNumber = 0
            let center = UNUserNotificationCenter.current()
            center.delegate = self
            center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
                  
                if granted{  // 点击允许推送
                       
                    printLog(message: "点击允许推送,成功")
                    center.getNotificationSettings { (settings) in
                           
                        DispatchQueue.main.async {
                            
                            application.registerForRemoteNotifications()
                        }
                        
                        printLog(message: "点击允许推送,成功++++\(settings)")
                    }
                }else{ // 点击不允许推送
                       
                    printLog(message: "点击不允许推送,注册失败")
                }
            }
    
        }
        
        
        
        /// 获取deviceToken成功 将得到的deviceToken传给环信SDK
        /// - Parameters:
        ///   - application: application
        ///   - deviceToken: deviceToken
        func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
            
            
            printLog(message:"获取deviceToke成功")
    
            DispatchQueue.global(qos: .default).async {
            
                EMClient.shared()?.registerForRemoteNotifications(withDeviceToken: deviceToken, completion: { (error) in
                    
                    if error != nil{
    
                        var errorDes = "上传失败,请重试"
                        switch (error!.code) {
                        case EMErrorUserNotFound:
                            errorDes = "用户ID不存在"
                            break
                        case EMErrorNetworkUnavailable:
                            errorDes = "网络未连接"
                            break
                        case EMErrorServerNotReachable:
                            errorDes = "无法连接服务器"
                            break
                        case EMErrorUserAuthenticationFailed:
                            errorDes = error!.errorDescription
                            break
                        case EMErrorUserLoginTooManyDevices:
                            errorDes = "登录设备数已达上限"
                            break
                        default:
                            break
                        }
    
                        printLog(message: "推送注册失败结果\(errorDes)  code\(error!.code)")
                    }else{
    
                        printLog(message: "推送注册成功")
                    }
                })
            }
        }
        
        
        
        /// 获取deviceToke 失败
        /// - Parameters:
        ///   - application: application
        ///   - error: error
        func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { // 可选
            printLog(message:"获取deviceToke 失败 Error: \(error)")
        }
        
        
        /// APP在前台得到通知
        /// - Parameters:
        ///   - center: center
        ///   - notification: notification
        ///   - completionHandler: completionHandler
        func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void){
            
            let userInfo = notification.request.content.userInfo
            printLog(message: "APP在前台得到推送\(userInfo)")
            completionHandler(.alert);
            EMClient.shared()?.application(UIApplication.shared, didReceiveRemoteNotification: userInfo)
            
            
            printLog(message: "保存条数\(Defaults[.unread_system_count])")
            let count = Defaults[.unread_system_count] + 1
            Defaults[.unread_system_count] = count
            TTIMClass.shared.setUnreadMessagesCount(count: Defaults[.unread_system_count])
            NotificationCenter.post(name: .SYSTEM_MSG_UNREADNUM, object: ["system_un_read_num":"\(Defaults[.unread_system_count])"], userInfo: nil)
            
            // 收到钱包通知,请求个人资金信息
            if "\(userInfo["f"] ?? "")" == "wallet"{
                
                TTNetworkBackgroundRequest.requestAccountData()
            }
        }
    
        
        /// 点击通知回调
        /// - Parameters:
        ///   - center: center
        ///   - response: response
        ///   - completionHandler: completionHandler
        func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void){
            
            let userInfo:Dictionary = response.notification.request.content.userInfo
            printLog(message: "userInfo\(userInfo))")
            
    
            /*   点击通知
                 ApnsNickName: 发送方设置的用户名(即环信管理后台中看到的用户昵称)
                 xxxx: 消息内容(发送方发的什么,就显示什么)
                 badge: 角标,表示离线消息数
                 sound: 收到 APNs 时的提示音
                 f: 消息发送方的环信 ID
                 t: 消息接收方的环信 ID
                 m: 消息 ID
             */
            if userInfo.keys.contains("f") {
    
                printLog(message: "点击通知")
    
                if "\(userInfo["f"] ?? "")" == "admin" || "\(userInfo["f"] ?? "")" == "wallet" { // 系统消息
    
                    TT_SINGLE_ONCE.pushMessageIndex = 1
                    NotificationCenter.post(name: .PUSHNOTIFICATION, object: "1", userInfo: nil)
                    
                    if "\(userInfo["f"] ?? "")" == "wallet"{
                        
                        TTNetworkBackgroundRequest.requestAccountData()
                    }
                }else{ // IM消息
    
                    TT_SINGLE_ONCE.pushMessageIndex = 0
                    NotificationCenter.post(name: .PUSHNOTIFICATION, object: "0", userInfo: nil)
                }
    
           
            //需要执行这个方法
            completionHandler()
        }
    

    APP在前台时收到了消息使用本地推送,本地推送显示的逻辑在TTIMClass类中已封装过。

    吐槽一下环信的推送,不是很及时在开发和测试过程中会产生很多的困扰。

    结尾

    项目中还使用了高德地图、阿里的人脸识别,自己都进行过二次封装,整个项目使用的MVVM模式,写之前参考了很多的设计思路,也遇到一些坑。以后有时间再写一篇记录下。

    最后:愿2020年疫情尽快结束,愿我中华国运昌盛,也愿自己少一些浮躁多一分沉淀,愿自己的能力和精神世界更加强大!

    end

    相关文章

      网友评论

          本文标题:iOS社交项目(使用环信二次开发)

          本文链接:https://www.haomeiwen.com/subject/kqrxfhtx.html