前言
时光如梭,2019转瞬即逝。去年手头上做了公司的两个新项目,一个教育平台类一个社交类,任务有点重但都挺过来了。社交类项目公司原计划春节后测试验收完毕开始投放市场,但今年伊始一场肺炎席卷了整个国家,也打乱了公司的部署。目前公司未安排线上办公,上班也是遥遥无期,看着房贷看着银行存款让我鸭梨山大。今天是2020年2月11日,啰嗦半天,闲来无事记录下社交项目中趟的坑。
集成环信
-
cocoapods管理环信SDK
我的项目中主要使用了环信的基本IM还有实时语音功能,但项目是用swift写的,所以自己写了一些供swift里使用的头文件,这里就不做赘述了。
- 项目中调用
环信官方demo代码写的很详细,可直接给代码文件copy进自己的项目中,使用很简单。
图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)")
}
}
}
}
- TTIMClass里写的isSystem变量,在APP活跃状态下区分消息是即时通信还是服务器推送消息。
TTIMClass页面跳转方法是根据自身项目的业务逻辑封装,里面所传model是为了在聊天页显示用户的头像、用户名和一些基本信息,因为环信的服务器不会记录集成方服务器的头像和用户名还有个人信息,所以给一个model来记录个人的一些信息,方便在聊天页和消息页使用。
如在聊天页界面使用,必须在聊天的信息里添加扩展信息,用来在即时消息里传递用户的信息。
如下图这样:
图2
图3
代码如下:
图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年疫情尽快结束,愿我中华国运昌盛,也愿自己少一些浮躁多一分沉淀,愿自己的能力和精神世界更加强大!