简单总结一下,仅对日后再次集成更加快速便捷,多数是对文档的简单概括.
一.快速集成环信SDK
1 注册环信账号,创建新应用,获取应用的AppKey.
2 参照官方文档,导入SDK:
(1).下载官网SDK (http://www.easemob.com/download/im:), 从官网上下载下来的包中分为如下五部分:
• 环信 iOS HyphenateSDK 开发使用(不包含实时通话功能)
• 环信 iOS HyphenateFullSDK 开发使用(包含实时通话功能)
• 环信 iOS doc SDK 相关API文档
• 环信 iOS ChatUIDemo3.0 工程源码
• 环信 iOS EaseUI 工程源码
• iOS chatdemo-ui-3.x.x.ipa 打包的 ipa
(2).将SDK文件夹(HyphenateSDK)拖入项目中, 并勾选上 Destination。
(其中,SDK 不支持 bitcode,向 Build Settings → Linking → Enable Bitcode 中设置 NO。)
添加以下库:
3 增加预编译头文件pch,保证头文件的全局引用.( 【Build Settings】中搜索框输入prefix header 将【Precompile Prefix Header】设置为【YES】,将PCH文件地址填写进【Prefix Header】中
方法一:也可以通过将PCH文件直接拖入【Prefix Header】框中.
方法二: "$(SRCROOT)/当前工程名字/需要包含头文件所在文件夹").
特别注意(否则会报一堆错):
导入<UIKit/UIKit.h>等OC框架时需要添加:
# ifdef OBJC
# endif
以上步骤经过command + b 编译没错,即代表导入环信SDK成功.
二.搭建界面实现单聊功能
初始化SDK:
在AppDelegate.m加入如下代码:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//AppKey:注册的AppKey,详细见下面注释。
//apnsCertName:推送证书名(不需要加后缀),详细见下面注释。
EMOptions *options = [EMOptions optionsWithAppkey:@"douser#istore"];
options.apnsCertName = @"istore_dev";
[[EMClient sharedClient] initializeSDKWithOptions:options];
return YES;
}
// APP进入后台
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[EMClient sharedClient] applicationDidEnterBackground:application];
}
// APP将要从后台返回
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[EMClient sharedClient] applicationWillEnterForeground:application];
}
简单的搭了一个界面:
按照文档加入以下代码:
注册事件:
- (IBAction)hdRegister:(id)sender {
//其中@"hd222"是自己设置的用户名
EMError *error = [[EMClient sharedClient] registerWithUsername:@"hd222" password:@"111111"];
if (error==nil) {
NSLog(@"注册成功");
}
}
登录事件:
- (IBAction)login:(id)sender {
//其中@"hd222"是自己设置的用户名
EMError *error = [[EMClient sharedClient] loginWithUsername:@"hd222" password:@"111111"];
if (!error) {
NSLog(@"登录成功");
}
}
退出事件:
- (IBAction)goOut:(id)sender {
EMError *error = [[EMClient sharedClient] logout:YES];
if (!error) {
NSLog(@"退出成功");
}
}
发送消息:
- (IBAction)sendMessage:(id)sender {
EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"要发送的消息"];
NSString *from = [[EMClient sharedClient] currentUsername];
//生成Message, 其中@"hd111"是自己设置的消息的接收方
EMMessage *message = [[EMMessage alloc] initWithConversationID:@"6001" from:from to:@"hd111" body:body ext:nil];
message.chatType = EMChatTypeChat;// 设置为单聊消息
//message.chatType = EMChatTypeGroupChat;// 设置为群聊消息
//message.chatType = EMChatTypeChatRoom;// 设置为聊天室消息
//发送Message
[[EMClient sharedClient].chatManager asyncSendMessage:message progress:nil completion:^(EMMessage *aMessage, EMError *aError) {}];
}
注意:
发送消息过程中,需要在viewDidLoad方法中添加EMChatManagerDelegate协议的代理.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];
}
一定不要忘了遵守协议:
调用接收消息的代理方法(直接复制粘贴即可):
#pragma mark -接收消息的代理方法
- (void)didReceiveMessages:(NSArray *)aMessages{
for (EMMessage *message in aMessages) {
EMMessageBody *msgBody = message.body;
switch (msgBody.type) {
case EMMessageBodyTypeText:
{
// 收到的文字消息
EMTextMessageBody *textBody = (EMTextMessageBody *)msgBody;
NSString *txt = textBody.text;
NSLog(@"收到的文字是 txt -- %@",txt);
}
break;
case EMMessageBodyTypeImage:
{
// 得到一个图片消息body
EMImageMessageBody *body = ((EMImageMessageBody *)msgBody);
NSLog(@"大图remote路径 -- %@" ,body.remotePath);
NSLog(@"大图local路径 -- %@" ,body.localPath); // // 需要使用sdk提供的下载方法后才会存在
NSLog(@"大图的secret -- %@" ,body.secretKey);
NSLog(@"大图的W -- %f ,大图的H -- %f",body.size.width,body.size.height);
NSLog(@"大图的下载状态 -- %lu",body.downloadStatus);
// 缩略图sdk会自动下载
NSLog(@"小图remote路径 -- %@" ,body.thumbnailRemotePath);
NSLog(@"小图local路径 -- %@" ,body.thumbnailLocalPath);
NSLog(@"小图的secret -- %@" ,body.thumbnailSecretKey);
NSLog(@"小图的W -- %f ,大图的H -- %f",body.thumbnailSize.width,body.thumbnailSize.height);
NSLog(@"小图的下载状态 -- %lu",body.thumbnailDownloadStatus);
}
break;
case EMMessageBodyTypeLocation:
{
EMLocationMessageBody *body = (EMLocationMessageBody *)msgBody;
NSLog(@"纬度-- %f",body.latitude);
NSLog(@"经度-- %f",body.longitude);
NSLog(@"地址-- %@",body.address);
}
break;
case EMMessageBodyTypeVoice:
{
// 音频sdk会自动下载
EMVoiceMessageBody *body = (EMVoiceMessageBody *)msgBody;
NSLog(@"音频remote路径 -- %@" ,body.remotePath);
NSLog(@"音频local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在(音频会自动调用)
NSLog(@"音频的secret -- %@" ,body.secretKey);
NSLog(@"音频文件大小 -- %lld" ,body.fileLength);
NSLog(@"音频文件的下载状态 -- %lu" ,body.downloadStatus);
NSLog(@"音频的时间长度 -- %lu" ,body.duration);
}
break;
case EMMessageBodyTypeVideo:
{
EMVideoMessageBody *body = (EMVideoMessageBody *)msgBody;
NSLog(@"视频remote路径 -- %@" ,body.remotePath);
NSLog(@"视频local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在
NSLog(@"视频的secret -- %@" ,body.secretKey);
NSLog(@"视频文件大小 -- %lld" ,body.fileLength);
NSLog(@"视频文件的下载状态 -- %lu" ,body.downloadStatus);
NSLog(@"视频的时间长度 -- %lu" ,body.duration);
NSLog(@"视频的W -- %f ,视频的H -- %f", body.thumbnailSize.width, body.thumbnailSize.height);
// 缩略图sdk会自动下载
NSLog(@"缩略图的remote路径 -- %@" ,body.thumbnailRemotePath);
NSLog(@"缩略图的local路径 -- %@" ,body.thumbnailLocalPath);
NSLog(@"缩略图的secret -- %@" ,body.thumbnailSecretKey);
NSLog(@"缩略图的下载状态 -- %lu" ,body.thumbnailDownloadStatus);
}
break;
case EMMessageBodyTypeFile:
{
EMFileMessageBody *body = (EMFileMessageBody *)msgBody;
NSLog(@"文件remote路径 -- %@" ,body.remotePath);
NSLog(@"文件local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在
NSLog(@"文件的secret -- %@" ,body.secretKey);
NSLog(@"文件文件大小 -- %lld" ,body.fileLength);
NSLog(@"文件文件的下载状态 -- %lu" ,body.downloadStatus);
}
break;
default:
break;
}
}
}
可以利用真机和模拟器之间发送消息测试,以上是简单的发送消息
单聊:
打开单聊界面按钮集成了基于EaseUI的单聊界面(建议当对界面要求不高的时候直接使用环信UI):
集成方式:
直接将EaseUI拖入已经集成SDK的项目中,然后进行初始化:
第 1 步:引入相关头文件 #import “EaseUI.h”。
第 2 步:在工程的 AppDelegate 中的以下方法中,调用 EaseUI 对应方法。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[EaseSDKHelper shareHelper] easemobApplication:application
didFinishLaunchingWithOptions:launchOptions
appkey:@"douser#istore"
apnsCertName:@"istore_dev"
otherConfig:@{kSDKConfigEnableConsoleLogger:[NSNumber numberWithBool:YES]}];
return YES;
}
直接创建聊天会话:
//打开单聊界面
- (IBAction)openChat:(id)sender {
//hd111是注册在真机上的账号 用于真机和模拟器实现聊天
EaseMessageViewController *chatController = [[EaseMessageViewController alloc] initWithConversationChatter:@"hd111" conversationType:EMConversationTypeChat];
chatController.title = @"环信单聊";
// UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:chatController];
[self.navigationController pushViewController:chatController animated:YES];
}
点击单聊界面按钮利用真机和模拟器即测试实现单聊功能.
swift代码:(处理头像)
import UIKit
class HuanXinChatViewController: EaseMessageViewController {
var blackArr:NSArray!
override init!(conversationChatter: String!, conversationType: EMConversationType) {
super.init(conversationChatter: conversationChatter, conversationType: conversationType)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: KRemoveMessageAlterNotification), object: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
self.dataSource = self
self.chatBarMoreView.removeItematIndex(3)
self.chatBarMoreView.removeItematIndex(3)
// EMAlbum
self.chatBarMoreView.updateItem(with: UIImage(named:"EMAlbum"), highlightedImage: UIImage(named:"EMAlbumClick"), title: "", at: 0)
self.chatBarMoreView.updateItem(with: UIImage(named:"EMLocation"), highlightedImage: UIImage(named:"EMLocationClick"), title: "", at: 1)
self.chatBarMoreView.updateItem(with: UIImage(named:"EMTakePhotos"), highlightedImage: UIImage(named:"EMTakePhotosClick"), title: "", at: 2)
IQKeyboardManager.sharedManager().enableAutoToolbar = false
//设置自己头像
if UserDefaults.standard.value(forKey: "headpath") == nil{
getUserHeadPic()
}
// print("打印头像路径\(UserDefaults.standard.value(forKey: "headpath"))")
getBlackList()
}
//获取头像
func getUserHeadPic(){
NetworkTool.GET(URLString: GETUSERINFO, parameters: nil, token: true, success: { (response) in
if let headPic = response["headpath"]{
UserDefaults.standard.setValue("\(OSS)\(headPic!)", forKey: "headpath")
}
}, failure: { (error) in
})
}
//黑名单
func getBlackList(){
var error:EMError? = nil
blackArr = EMClient.shared().contactManager.getBlackListFromServerWithError(&error) as NSArray!
// if blackList?.count == 0{
//
// }else{
//
// }
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension HuanXinChatViewController:EaseMessageViewControllerDataSource,EaseMessageViewControllerDelegate{
//不显示已读
func messageViewControllerShouldMarkMessages(asRead viewController: EaseMessageViewController!) -> Bool {
return true
}
// func messageViewController(_ viewController: EaseMessageViewController!, shouldSendHasReadAckFor message: EMMessage!, read: Bool) -> Bool {
// return false
// }
func messageViewController(_ viewController: EaseMessageViewController!, modelFor message: EMMessage!) -> IMessageModel! {
let model = EaseMessageModel.init(message: message)
let currentName = EMClient.shared().currentUsername
let messageName = model?.message.from
let messageTo = model?.message.to
print("打印环信消息的拓展\(model?.messageStatus)")
if model?.messageStatus == EMMessageStatusFailed{
print("信息发送失败或已被屏蔽")
// Alert.showBottomWithText(text: "信息发送失败或已被屏蔽", bottomOffset: 200, duration: 5)
//
}
// print("打印环信消息的拓展\(messageTo)")
if currentName! == messageName {
if blackArr.contains(currentName!) {
// Alert.showText(text: "信息发送失败或已被屏蔽")
print("信息发送失败或已被屏蔽")
}
model?.avatarURLPath = UserDefaults.standard.value(forKey: "headpath") as! String!
model?.nickname = UserDefaults.standard.value(forKey: "userName") as! String!
model?.failImageName = "icon_avatar"
}else{
// print("打印环信消息的拓展\(messageName)")
if message.ext != nil{
// print("打印拓展消息的字典\((message.ext as Dictionary)["userName"])")
if (message.ext as Dictionary)["userName"] != nil{
model?.nickname = (message.ext as? Dictionary)?["userName"]
}
model?.avatarURLPath = (message.ext as? Dictionary)?["userPic"]
model?.failImageName = "icon_avatar"
}
}
// http://oss.hp.etong.tech/images/ed2cf47424a55b2a213316b8ab68b535.png
return model
}
//点击头像
func messageViewController(_ viewController: EaseMessageViewController!, didSelectAvatarMessageModel messageModel: IMessageModel!) {
// let userProfile = UserProfileViewController()
print("点击头像啦")
}
}
环信获取消息的未读数量
//获取消息的未读数量
func getUnReadNum() -> Int {
var unReadObj = 0
if UserDefaults.standard.value(forKey: "token") != nil {
if EMClient.shared().isLoggedIn == true{
if EMClient.shared().chatManager != nil{
let conversations: NSArray? = EMClient.shared().chatManager.getAllConversations() as NSArray?
if (conversations != nil && conversations!.count > 0) {
for conversation: EMConversation in conversations as! [EMConversation] {
unReadObj += Int(conversation.unreadMessagesCount)
}
}
}
}
}
return unReadObj
}
实时收到消息的代理:
//环信聊天代理
extension AppDelegate:EMChatManagerDelegate{
func messagesDidReceive(_ aMessages: [Any]!) {
//收到新消息后 提示音
playSoundEffect("sound.caf")
if aMessages.count > 0 {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: KReceivedNewMessageNotification), object: nil)
}
for message in aMessages {
let messageMo = message as! EMMessage
let msgBody = messageMo.body
if msgBody?.type == EMMessageBodyTypeText{
let textBody = msgBody as! EMTextMessageBody
messageDetail = textBody.text!
}else if msgBody?.type == EMMessageBodyTypeImage{
messageDetail = "[图片]"
}else if msgBody?.type == EMMessageBodyTypeLocation{
messageDetail = "[位置]"
}else if msgBody?.type == EMMessageBodyTypeVoice{
messageDetail = "[语音消息]"
}else if msgBody?.type == EMMessageBodyTypeVideo{
messageDetail = "[视频]"
}
}
}
}
音效
/** 收到新消息播放音效文件 */
func playSoundEffect(_ name: String) {
// 获取音效
let audioFile: String? = Bundle.main.path(forResource: name, ofType: nil)
let fileUrl = URL(fileURLWithPath: audioFile!)
// 1、获得系统声音ID
var soundID: SystemSoundID = 0
AudioServicesCreateSystemSoundID(((fileUrl as CFURL) ), &soundID)
// 2、播放音频
AudioServicesPlaySystemSound(soundID)
}