首先第一思路肯定是去官方文档和 demo,但是环信官方文档在实现聊天页面和好友模块并不详细,demo 功能非常强大,但很复杂,挑拣部分功能就显得格外困难,并且项目结构有些混乱,很难理出头绪。我在这里浪费了很长时间,强烈建议大家使用IOS快速集成环信IM ,作者和官方 demo 保持同步,无须担忧。作者采用的是拖入文件夹的方式,如果有想要用 pod 的方式, 可以参考一下我的github
官网 demo 的几个问题在这里强调一下,引起重视
问题1
引用Parse.framework、Bolts.framework时项目容易出错或出现Not found问题,其实这两个库并不是必须的,而且Facebook已经确定在2017年1月份停止提供Parse服务。
解决方案:删除Parse相关类,用 UserCacheManager替代管理用户本地缓存,用UserWebManager管理后端云缓存。问题2
ChatDemoHelper辅助类集成了很多聊天相关界面的操作方法,开发者一般会直接复用,但是ChatDemoHelper对MainViewController的函数依赖度比较高
解决方案:1.将ChatDemoHelper中的mainVC类型更换成UIViewController;2.将MainController中的几个方法用通知(NSNotificationCenter)实现;问题3
聊天相关页面与业务逻辑页面放在同一目录中,对于开发者来说,需要分拣; 解决方案:将环信相关的文件、资源统一放在【ChatUI】和【ChatSDK】目录中,方便开发者直接拖拽这两个文件夹即可快速集成聊天功能。
1.按照 IOS快速集成环信IM 集成环信
注意:在pch 文件中加上 #import "YYCategories.h"
2. linked command failed 问题
2.1 "OBJC_CLASS$_CMMotionManager", referenced from:objc-class-ref in AlipaySDK
解决方法:遇到这样的错误,是因为少了CoreMotion.framework
Build Phases ->Link Binary With Libraries 下添加库: CoreMotion.framework。
2.2'_compress2', referenced from: +[UMANUtil deflatedDataPrefixedWith:level:source:] in libMobClickLibrary.a(UMANUtil.o)
解决方法:这是友盟统计的错误,在Other Linker Flags里加入-lz然后再编译通过。
类似的其他
'_compress2', referenced from:
'_inflateReset', referenced from:
'inflateInit', referenced from:
'_inflateEnd', referenced from:
'inflateInit2', referenced from:
均可以通过-lz来解决。
-lz 会让你的程序在编译的时候against the built-in zlib,从而解决问题
2.3 . _rr_process_pcm in libHyphenateVideoRecorderPlugin.a(rav_record.o)
解决办法:这个是因为项目中少了libffmpeg-ios-full.a。如果你的项目不需要实时视频通话时,录制对方视频的功能,那么可以把plugin文件夹去掉。在新版demo中HyphenateVideoRecoder文件夹提供了libffmpeg-ios-full.a。plugin in 在 demo的class/call 文件夹内。
2.4-[AVSRWebSocket _innerPumpScanner] in AVOSCloud(AVSRWebSocket.o)
去掉警告中的第三方库, 黄色警告问题解决。加入包含库的文件夹之后又删除就容易出现这个问题。
Framework Search Paths 管理导入的.framework的路径
Library Search Paths 管理导入的.a的路径
官方库漏掉了一些库, 按照 IOS快速集成环信IM 教程中的补全即可
3.代码崩溃问题
3.1 NSInvalidArgumentException', reason: '-[EMClient callManager]: unrecognized selector sent to instance 0x6040005baa20'
删除 HyphenateSDK 即可
3.2 '-[EMVoiceMessageBody text]: unrecognized selector sent to instance 0x60000047c7c0'
缺少库,添加缺失的库
4.其他问题
4.1 聊天点击相册、照相、定位崩溃问题
这是 plist 中没有申请权限,这个问题坑了我好久,我一直以为是缺少库的问题,不过也的确缺少库😅
//如果遇到头像不显示的问题, 设置允许所有网络请求
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSCameraUsageDescription</key>
<string>demo需要使用您的相机</string>
<key>NSLocationAlwaysUsageDescription</key>
<string></string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>demo需要使用您的位置信息</string>
<key>NSMicrophoneUsageDescription</key>
<string>demo需要使用您的麦克风</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>demo需要访问您的相册</string>
4.2 ios10 聊天点击发送文件崩溃
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', Application initializing document picker is missing the iCloud entitlement
如果您想让其他应用程序访问存储在iCloud容器中的文件,则必须启用iCloud Drive支持。在您的应用程序可以使用文档选取器之前,您必须打开Xcode中的iCloud Documents功能。有关iCloud文档的更多信息,请参阅在iCloud中设计文档。
4.3 聊天点击发送语音没有响应
- 对照 chatDemoSimple 补全 Framework, IOS快速集成环信IM
2.看源代码排查问题
原因在于
AVAuthorizationStatusNotDetermined
没有弹出授权对话框, 但是 plist 中已经添加了判断,目前真机可以顺利使用, 只有模拟器中有这个问题。如果大牛发现了问题的原因, 欢迎留言指点😌5.修改旧项目遇到的问题
5.1 Xcode编译工程时出现Unknown type name 'NSString' 错误
在所在项目pch文件中,把自己所#import的头文件做如下操作这个一般是预编译问题,只需要在预编译文件加上。我的项目因为是老项目,一时马虎没有看到项目中底部有多余一个OBJC,导致了这个报错。
#ifdef __OBJC__ //首句
#import <UIKit/UIKit.h> //这个是你预编译的文件举例。
#endif //尾句
5.2 ld: library not found for -lDACircularProgress
之前我使用 pod 引入的 EaseUI 所以出现了这个问题
解决办法:other linker flag 去掉 EaseUI 绑定的第三方库
-l"DACircularProgress"
-l”EaseUI" 等等
6.头像和昵称不显示的问题
草草们的忧伤:环信IM昵称和头像 已经说得很详细了,我记录一下实现从服务器获取好友的昵称与头像功能。同样是存到LeanCloud中,在登陆成功后直接从服务器中获取进行存储。
#pragma mark - 下载用户头像和昵称
//自家服务器的 userId 是自家数据库的 userid
//环信的 username, 也是登录页面的 username, 是手机号
- (void)loadUserInfo:(NSString *)username{
NSString *urlStr = [OPENAPIHOST stringByAppendingString:@"user_move/getuserinfo"];
NSDictionary *parameter = @{@"userid": self.userid};
[[NetworkManger shareManger] request:POST URLString:urlStr parameters:parameter finished:^(id result, NSError *error) {
if (error != nil){
return;
}
NSDictionary *resultDict = result;
MineModel *mineModel = [MineModel mj_objectWithKeyValues:resultDict[@"row"]];
//关键性的就是这几句
NSString *userOpenId = username;// 用户环信ID
NSString *nickName = mineModel.realname; // 用户昵称
// NSString *avatarUrl = [OPENAPIHOST_IMAGE stringByAppendingString:mineModel.headimg];
NSString *avatarUrl = [NSString stringWithFormat:@"http://duoroux.com/chat/avatar/%d.jpg",arc4random()%10];// 用户头像(绝对路径)
// 登录成功后,如果后端云没有缓存用户信息,则新增一个用户
[UserWebManager createUser:userOpenId nickName:nickName avatarUrl:avatarUrl];
// 通过消息的扩展属性传递昵称和头像时,需要调用这句代码缓存
[UserCacheManager save:userOpenId avatarUrl:avatarUrl nickName:nickName];
}];
}
/*liststr
user_move/getUserList
[{"phone":"13103110311","id":1,"realname":"超管"},{"phone":"15003110001","id":26,"realname":"测试安"}]
*/
- (void)loadContactList:(NSString *)username{
NSString *urlStr = [OPENAPIHOST stringByAppendingString:@"user_move/getUserList"];
NSDictionary *parameter = @{@"liststr":self.userid};
[[NetworkManger shareManger]request:POST URLString:urlStr parameters:parameter callback:^(NetworkResult resultCode, id responseObject) {
if (resultCode != NetworkResultSuceess){
[self showHint:(NSString *)responseObject];
return;
}
DebugLog(@"%@",responseObject);
NSDictionary *resultDict = responseObject;
NSArray *resultArray = resultDict[@"row"];
if (resultArray.count <= 0){
return;
}
for (NSDictionary *dict in resultArray){
MineModel *mineModel = [MineModel mj_objectWithKeyValues:dict];
NSString *userOpenId = username;// 用户环信ID
NSString *nickName = mineModel.realname; // 用户昵称
NSString *avatarUrl = [OPENAPIHOST_IMAGE stringByAppendingString:mineModel.headimg];
// 登录成功后,如果后端云没有缓存用户信息,则新增一个用户
[UserWebManager createUser:userOpenId nickName:nickName avatarUrl:avatarUrl];
//通过消息的扩展属性传递昵称和头像时,需要调用这句代码缓存
[UserCacheManager save:userOpenId avatarUrl:avatarUrl nickName:nickName];
}
}];
}
还有这样一种方法, 把用户基本的昵称和头像的URL放到消息的扩展中,通过消息传递给接收方,当收到一条消息时,则能通过消息的扩展得到发送者的昵称和头像URL,然后保存到本地数据库和缓存。
这样改动的代码比较多,我没有用,感兴趣的可以看看这篇文章 IOS中如何显示开发者服务器上的昵称和头像