iOS

iOS

0
评论

NIUDAY 11.23 北京站 |环信CEO刘俊彦现场讲述人工智能赋能客户互动之现状与未来 刘俊彦 人工智能 环信 七牛云

beyond 发表了文章 • 50 次浏览 • 2018-11-15 16:36 • 来自相关话题

2018 年是见证「奇迹」的一年。AI 从多年的热门话题中开始走下神坛,逐渐深入到了各个行业,加速经济结构优化及行业智慧化升级,AI 已不再是难以企及的神话而是可触摸的美好未来。 政策支持加上资本推动,无论是从新兴行业还是传统行业都出现了人工智能方面的布局者和佼佼者,智慧教育、移动社交、智能语音、智慧客服、传统媒体等行业都在突破技术上和流程上的难点和困惑。

11 月 23 日,一场由七牛云主办主题为「AI 产业技术的渗透与融合」的 NIUDAY 小牛汇共享日将在北京举行。会上,将邀请众多行业内知名企业及技术专家,针对当前 AI 在技术上以及行业中遇到的一些壁垒问题,进行深入探讨和分享。

智慧教育、移动未来,体验指尖上的 AI

在此次活动中,除行业专家的视点剖析之外,对于 AI 与行业的渗透与融合度是理论大于实践亦或是理论与实践已完美结合,已经应用的行业代表案例有哪些?对于这些问题,大家可以在此次活动中得以解惑。




刘俊彦
手机电商、手机软件系统的快速更迭让在线客服、移动客服有了更大的发展空间,成了市场中的一片蓝海,究竟现状如何、客户体验的真实反馈是什么?环信 CEO 刘俊彦先生带来的《人工智能赋能客户互动之现状与未来》让你更贴近生活,更能体会 AI 的无处不在。




谢华亮

科技发展教育先行,技术与教育的结合又将碰撞出怎样的火花,会上,将有好未来 SEG 智慧教育事业部技术总监谢华亮带来主题为《AI 在教育行业中的应用》的精彩演讲。




佘超杰

移动互联网的发展已影响到我们身边的每一个人,游戏、娱乐、社交等手机平台的火爆更是带来了巨大的信息流量。但如何应对这些突然爆发的信息流,如何让产品与技术更好地去结合以吸引更多的受众人群,成为人们关注的话题。以作为专注于移动互联网社交的知名企业 Blued 为例,实践与技术并重的技术专家佘超杰将分享给我们《AI 在 Blued 上的应用》。
                
连线行业专家,洞悉专业视点

人工智能技术在飞速发展过程中得到了国家以及政府的极大关注与大力支持,中国人工智能产业发展联盟作为国家发展改革委、科学技术部、工业和信息化部、中央网信办四部委共同指导下的人工智能产业权威联盟机构,也将加入到此次 NIUDAY 小牛汇共享日活动中来。





孙明俊

中国信息通信研究院人工智能部副主任,中国人工智能产业发展联盟总体组组长、数据中心联盟秘书长孙明俊女士将出席本次活动,并将从行业专家的角度带来主题为《人工智能产业发展水平分析》的演讲,向大家解析产业发展水平、分享行业利好政策等。

战略签约中国网 传统与创新的再突破





杨新华

值得一提的是,本次 NIUDAY 小牛汇共享日活动上七牛云将与中国互联网新闻中心举行战略合作签约仪式。届时,中国互联网新闻中心·中国网副总编辑杨新华先生将带来《AI 如何讲好中国故事》的演讲,介绍七牛云在未来媒体平台构建中发挥的作用,以及正处于变革中的媒体行业对 AI 的思考。

当然作为本次 NIUDAY 小牛汇活动的主办方,七牛云也准备了满满的干货带给大家。七牛云技术总监陈超《数据智能时代的智慧工厂实践》、七牛云人工智能实验室资深产品经理杨叶青《一站式审核助力无忧运营》带你体会七牛云两大重要产品线的技术与发展。

更多大咖嘉宾,请往下看~ 





关于环信机器人:

环信机器人已经在中通快递、新东方、天津农商行、宜家、环球捕手等头部企业的“双十一”中发挥着中流砥柱的作用,机器人可以帮助解决80%以上的常见问题,在售后环节能替代超90%的人工,轻松实现7*24无间断客户服务,AI从此让我们的客服人员告别苦逼,云淡风轻,笑对各种节假日促销、狂欢节。

目前,环信机器人已经广泛服务于包括保险、证券、银行、教育、物流、商旅、电商、汽车等行业的数万客户,日机器人会话超千万条。
活动报名地址:http://www.huodongxing.com/event/2464773427600 查看全部

2018 年是见证「奇迹」的一年。AI 从多年的热门话题中开始走下神坛,逐渐深入到了各个行业,加速经济结构优化及行业智慧化升级,AI 已不再是难以企及的神话而是可触摸的美好未来。 政策支持加上资本推动,无论是从新兴行业还是传统行业都出现了人工智能方面的布局者和佼佼者,智慧教育、移动社交、智能语音、智慧客服、传统媒体等行业都在突破技术上和流程上的难点和困惑。



11 月 23 日,一场由七牛云主办主题为「AI 产业技术的渗透与融合」的 NIUDAY 小牛汇共享日将在北京举行。会上,将邀请众多行业内知名企业及技术专家,针对当前 AI 在技术上以及行业中遇到的一些壁垒问题,进行深入探讨和分享。

智慧教育、移动未来,体验指尖上的 AI

在此次活动中,除行业专家的视点剖析之外,对于 AI 与行业的渗透与融合度是理论大于实践亦或是理论与实践已完美结合,已经应用的行业代表案例有哪些?对于这些问题,大家可以在此次活动中得以解惑。

微信图片_20181115162453.jpg

刘俊彦


手机电商、手机软件系统的快速更迭让在线客服、移动客服有了更大的发展空间,成了市场中的一片蓝海,究竟现状如何、客户体验的真实反馈是什么?环信 CEO 刘俊彦先生带来的《人工智能赋能客户互动之现状与未来》让你更贴近生活,更能体会 AI 的无处不在。

微信图片_20181115162531.jpg

谢华亮



科技发展教育先行,技术与教育的结合又将碰撞出怎样的火花,会上,将有好未来 SEG 智慧教育事业部技术总监谢华亮带来主题为《AI 在教育行业中的应用》的精彩演讲。

微信图片_20181115162544.jpg

佘超杰



移动互联网的发展已影响到我们身边的每一个人,游戏、娱乐、社交等手机平台的火爆更是带来了巨大的信息流量。但如何应对这些突然爆发的信息流,如何让产品与技术更好地去结合以吸引更多的受众人群,成为人们关注的话题。以作为专注于移动互联网社交的知名企业 Blued 为例,实践与技术并重的技术专家佘超杰将分享给我们《AI 在 Blued 上的应用》。
                
连线行业专家,洞悉专业视点

人工智能技术在飞速发展过程中得到了国家以及政府的极大关注与大力支持,中国人工智能产业发展联盟作为国家发展改革委、科学技术部、工业和信息化部、中央网信办四部委共同指导下的人工智能产业权威联盟机构,也将加入到此次 NIUDAY 小牛汇共享日活动中来。

微信图片_20181115162600.jpg


孙明俊



中国信息通信研究院人工智能部副主任,中国人工智能产业发展联盟总体组组长、数据中心联盟秘书长孙明俊女士将出席本次活动,并将从行业专家的角度带来主题为《人工智能产业发展水平分析》的演讲,向大家解析产业发展水平、分享行业利好政策等。

战略签约中国网 传统与创新的再突破

微信图片_20181115162615.jpg


杨新华



值得一提的是,本次 NIUDAY 小牛汇共享日活动上七牛云将与中国互联网新闻中心举行战略合作签约仪式。届时,中国互联网新闻中心·中国网副总编辑杨新华先生将带来《AI 如何讲好中国故事》的演讲,介绍七牛云在未来媒体平台构建中发挥的作用,以及正处于变革中的媒体行业对 AI 的思考。

当然作为本次 NIUDAY 小牛汇活动的主办方,七牛云也准备了满满的干货带给大家。七牛云技术总监陈超《数据智能时代的智慧工厂实践》、七牛云人工智能实验室资深产品经理杨叶青《一站式审核助力无忧运营》带你体会七牛云两大重要产品线的技术与发展。

更多大咖嘉宾,请往下看~ 

5bed2dcd27fe6.jpg

关于环信机器人:

环信机器人已经在中通快递、新东方、天津农商行、宜家、环球捕手等头部企业的“双十一”中发挥着中流砥柱的作用,机器人可以帮助解决80%以上的常见问题,在售后环节能替代超90%的人工,轻松实现7*24无间断客户服务,AI从此让我们的客服人员告别苦逼,云淡风轻,笑对各种节假日促销、狂欢节。

目前,环信机器人已经广泛服务于包括保险、证券、银行、教育、物流、商旅、电商、汽车等行业的数万客户,日机器人会话超千万条。
活动报名地址:http://www.huodongxing.com/event/2464773427600
10
回复

收集基于环信SDK开发的开源项目 开源项目

Tayler 回复了问题 • 13 人关注 • 11306 次浏览 • 2018-10-30 17:59 • 来自相关话题

4
评论

【开源项目】全国首个开源直播小程序源码 环信公开课 小程序 直播

beyond 发表了文章 • 3066 次浏览 • 2018-07-20 17:30 • 来自相关话题

今天你看直播了吗?拥有10亿微信生态用户的小程序已经成为了继移动互联后的又一个现象级风口,随着微信小程序对外开放实时音视频录制及播放等更多连接能力,小程序与直播强强联合,在各行各业找到了非常多的玩法,小程序直播相比微信直播和APP直播更加简洁、流畅、低延时、多入口等众多优势迅速向商业直播领域及泛娱乐直播领域蔓延。从小游戏、内容付费、工具、大数据、社交电商创业者到传统品牌商们,都在努力搭上小程序直播这辆快车,以免错过微信生态里新的流量洼地。
 





作为一名环信生态圈资深开发者,本着对技术的热衷,对环信的眷恋和对党的忠诚,基于环信即时通讯云写了“直播购物小程序”,目前项目源码已全部免费开放,希望对有需求的企业和开发者提供一个思路和参考。
直播购物小程序源码github地址:https://github.com/YuTongNetworkTechnology/wechat_live/tree/master 
git打不开可直接点下面链接下载


小程序直播demo_2018-06-21.zip







直播购物小程序运行预览图 
 
小程序体验指南(仅需两步):
 
1、下载微信小程序开发工具,下载地址:https://developers.weixin.qq.c ... .html 
 




2、导入源码:将附件的源码解压直接导入 







环信小程序直播技术文档
一、 使用的技术
1、 环信IM直播室。
2、 微信小程序实时音视频播放组件live-player。
3、 推流软件(obs、易推流)等推流。
4、 视频流服务器(UCLOUD、七牛、腾讯)等视频流服务器。
二、 系统使用流程。
1、 视频推流软件将视频流推到流服务器。
2、 打开视频直播demo小程序注册环信账号。
3、 进入软件直播室进行测试。
三、 技术流程及使用的SDk
1、 注册环信账号
打开https://www.easemob.com/ 环信官网,点击右上角注册按钮,选择[注册即时通讯云]




填写对相关信息进行注册





注册成功后进行登录




注:新注册用户需进行账号的认证。
2、 直播应用创建
登录成功点击应用列表选择创建应用




输入应用名称等信息
 





创建成功后点击应用进入





需要注意的是应用的OrgName 和AppName这两个是以后都需要用到的两个参数变量




3、 直播创建
1)在创建直播之前需要对应用进行设置首先需要设置应用的直播流地址
第一步获取应用管理员的Tokencurl -X POST "https://a1.easemob.com/[应用OrgName]/[应用AppName]/token" -d '{"grant_type":"client_credentials","client_id":"[应用client_id]","client_secret":"[应用] client_secret"}'返回格式{
"access_token":"YWMtWY779DgJEeS2h9OR7fw4QgAAAUmO4Qukwd9cfJSpkWHiOa7MCSk0MrkVIco",
"expires_in":5184000,
"application":"c03b3e30-046a-11e4-8ed1-5701cdaaa0e4"












第二步设置直播流地址curl -X POST -H "Authorization: Bearer [管理员Token]" " https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms/stream_url -d '{"pc_pull":"[pc拉流地址]","pc_push":"[pc推流地址]","mobile_pull":"[手机拉流地址]","mobile_push":"[手机推流地址]"}'"成功返回格式:{
"action": "post",
"application": "e1a09de0-0e03-11e7-ad8e-a1d913615409",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"pc_pull": true,
"mobile_push": true,
"mobile_pull": true,
"pc_push": true
},
"timestamp": 1494084474885,
"duration": 1,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












2)创建主播
点击IM用户





点击注册IM用户





填写用户信息





创建用户的过程同样也可以通过REST API形式进行curl -X POST -i " https://a1.easemob.com/[应用OrgName]/[应用AppName]/users" -d '{"username":"[用户名]","password":"[密码]"}'
注:应用必须为开放注册





将注册的用户添加为主播curl -X POST -H "Authorization: [管理员Token]" https://a1.easemob.com/[应用OrgName]/[应用AppName]/super_admin -d'{"superadmin":"[IM用户名]"}'返回结果示例:{
"action": "post",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"result": "success"
},
"timestamp": 1496236798886,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












3)创建直播
点击直播





点击新建房间





填写房间信息




创建房间同时也可以使用REST API形式进行详情可以查看http://docs.easemob.com/im/live/server-integration环信官方文档。
4、 小程序demo集成使用
小程序直播购物demo集成官方WebIM SDK详情请查看https://github.com/easemob/webim-weixin-xcx
Demo具体配置如下
打开demo 下sdk配置文件





修改appkey为自己应用的appkey





打开pages/live/index.js修改房间默认拉流地址及直播间房间号





四、 扩展说明
Demo中房间为固定测试房间,实际使用中应获取环信直播的房间信息及房间列表。具体如下:
获取直播间列表:curl -X GET -H "Authorization: Bearer [用户Token]" https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms?ongoing=true&limit=[获取数量]&cursor=[游标地址(不填写为充开始查询)]
响应:{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"params": {
"cursor": [
"ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6MzE"
],
"ongoing": [
"true"
],
"limit": [
"2"
]
},
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": [
{
"id": "1924",
"chatroom_id": "17177265635330",
"title": "具体了",
"desc": "就咯",
"startTime": 1495779917352,
"endTime": 1495779917352,
"anchor": "wuls",
"gift_count": 0,
"praise_count": 0,
"current_user_count": 8,
"max_user_count": 9,
"status": "ongoing",
"cover_picture_url": "",
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1"
},
{
"id": "1922",
"chatroom_id": "17175003856897",
"title": "香山",
"desc": "随便",
"startTime": 1495777760957,
"endTime": 1495777760957,
"anchor": "sx001",
"gift_count": 0,
"praise_count": 8,
"current_user_count": 1,
"max_user_count": 3,
"status": "ongoing",
"cover_picture_url": "http://127.0.0.1:8080/easemob- ... ot%3B,
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1"
}
],
"timestamp": 1496303336669,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"cursor": "ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6NDk",
"count": 2
}












获取直播间详情:curl -X GET -H "Authorization: Bearer [用户Token]" " https://a1.easemob.com/[应用OrgName]/[应用AppName]/[房间id]/status"响应:{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"liveRoomID": "1946",
"status": "ongoing"
},
"timestamp": 1496234759930,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"count": 0
}














 
使用环信直播购物小程序遇到任何问题欢迎跟帖讨论。 查看全部
今天你看直播了吗?
拥有10亿微信生态用户的小程序已经成为了继移动互联后的又一个现象级风口,随着微信小程序对外开放实时音视频录制及播放等更多连接能力,小程序与直播强强联合,在各行各业找到了非常多的玩法,小程序直播相比微信直播和APP直播更加简洁、流畅、低延时、多入口等众多优势迅速向商业直播领域及泛娱乐直播领域蔓延。从小游戏、内容付费、工具、大数据、社交电商创业者到传统品牌商们,都在努力搭上小程序直播这辆快车,以免错过微信生态里新的流量洼地。
 
微信图片_20180725162426.jpg


作为一名环信生态圈资深开发者,本着对技术的热衷,对环信的眷恋和对党的忠诚,基于环信即时通讯云写了“直播购物小程序”,目前项目源码已全部免费开放,希望对有需求的企业和开发者提供一个思路和参考。
直播购物小程序源码github地址:https://github.com/YuTongNetworkTechnology/wechat_live/tree/master 
git打不开可直接点下面链接下载



预览图.jpg

直播购物小程序运行预览图 
 
小程序体验指南(仅需两步):
 
1、下载微信小程序开发工具,下载地址:https://developers.weixin.qq.c ... .html 
 
Catch9A07(07-20-17-38-30).jpg

2、导入源码:将附件的源码解压直接导入 


Catch1C69(07-20-17-38-30).jpg


环信小程序直播技术文档
一、 使用的技术
1、 环信IM直播室。
2、 微信小程序实时音视频播放组件live-player。
3、 推流软件(obs、易推流)等推流。
4、 视频流服务器(UCLOUD、七牛、腾讯)等视频流服务器。
二、 系统使用流程。
1、 视频推流软件将视频流推到流服务器。
2、 打开视频直播demo小程序注册环信账号。
3、 进入软件直播室进行测试。
三、 技术流程及使用的SDk
1、 注册环信账号
打开https://www.easemob.com/ 环信官网,点击右上角注册按钮,选择[注册即时通讯云]
1.png

填写对相关信息进行注册

2.png

注册成功后进行登录
3.png

注:新注册用户需进行账号的认证。
2、 直播应用创建
登录成功点击应用列表选择创建应用
4.png

输入应用名称等信息
 

5.png

创建成功后点击应用进入

6.png

需要注意的是应用的OrgName 和AppName这两个是以后都需要用到的两个参数变量
7.png

3、 直播创建
1)在创建直播之前需要对应用进行设置首先需要设置应用的直播流地址
第一步获取应用管理员的Token
curl -X POST "https://a1.easemob.com/[应用OrgName]/[应用AppName]/token" -d '{"grant_type":"client_credentials","client_id":"[应用client_id]","client_secret":"[应用] client_secret"}'
返回格式
{
"access_token":"YWMtWY779DgJEeS2h9OR7fw4QgAAAUmO4Qukwd9cfJSpkWHiOa7MCSk0MrkVIco",
"expires_in":5184000,
"application":"c03b3e30-046a-11e4-8ed1-5701cdaaa0e4"












第二步设置直播流地址
curl -X POST -H "Authorization: Bearer [管理员Token]"  " https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms/stream_url -d '{"pc_pull":"[pc拉流地址]","pc_push":"[pc推流地址]","mobile_pull":"[手机拉流地址]","mobile_push":"[手机推流地址]"}'"
成功返回格式:
{
"action": "post",
"application": "e1a09de0-0e03-11e7-ad8e-a1d913615409",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"pc_pull": true,
"mobile_push": true,
"mobile_pull": true,
"pc_push": true
},
"timestamp": 1494084474885,
"duration": 1,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












2)创建主播
点击IM用户

8.png

点击注册IM用户

9.png

填写用户信息

10.png

创建用户的过程同样也可以通过REST API形式进行
curl -X POST -i " https://a1.easemob.com/[应用OrgName]/[应用AppName]/users" -d '{"username":"[用户名]","password":"[密码]"}'

注:应用必须为开放注册

11.png

将注册的用户添加为主播
curl -X POST -H "Authorization: [管理员Token]"  https://a1.easemob.com/[应用OrgName]/[应用AppName]/super_admin -d'{"superadmin":"[IM用户名]"}'
返回结果示例:
{
"action": "post",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"result": "success"
},
"timestamp": 1496236798886,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












3)创建直播
点击直播

12.png

点击新建房间

13.png

填写房间信息
14.png

创建房间同时也可以使用REST API形式进行详情可以查看http://docs.easemob.com/im/live/server-integration环信官方文档。
4、 小程序demo集成使用
小程序直播购物demo集成官方WebIM SDK详情请查看https://github.com/easemob/webim-weixin-xcx
Demo具体配置如下
打开demo 下sdk配置文件

15.png

修改appkey为自己应用的appkey

16.png

打开pages/live/index.js修改房间默认拉流地址及直播间房间号

17.png

四、 扩展说明
Demo中房间为固定测试房间,实际使用中应获取环信直播的房间信息及房间列表。具体如下:
获取直播间列表:
curl -X GET -H "Authorization: Bearer  [用户Token]"  https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms?ongoing=true&limit=[获取数量]&cursor=[游标地址(不填写为充开始查询)]

响应:
{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"params": {
"cursor": [
"ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6MzE"
],
"ongoing": [
"true"
],
"limit": [
"2"
]
},
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": [
{
"id": "1924",
"chatroom_id": "17177265635330",
"title": "具体了",
"desc": "就咯",
"startTime": 1495779917352,
"endTime": 1495779917352,
"anchor": "wuls",
"gift_count": 0,
"praise_count": 0,
"current_user_count": 8,
"max_user_count": 9,
"status": "ongoing",
"cover_picture_url": "",
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1"
},
{
"id": "1922",
"chatroom_id": "17175003856897",
"title": "香山",
"desc": "随便",
"startTime": 1495777760957,
"endTime": 1495777760957,
"anchor": "sx001",
"gift_count": 0,
"praise_count": 8,
"current_user_count": 1,
"max_user_count": 3,
"status": "ongoing",
"cover_picture_url": "http://127.0.0.1:8080/easemob- ... ot%3B,
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1"
}
],
"timestamp": 1496303336669,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"cursor": "ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6NDk",
"count": 2
}












获取直播间详情:
curl -X GET -H "Authorization: Bearer [用户Token]" " https://a1.easemob.com/[应用OrgName]/[应用AppName]/[房间id]/status"
响应:
{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"liveRoomID": "1946",
"status": "ongoing"
},
"timestamp": 1496234759930,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"count": 0
}














 
使用环信直播购物小程序遇到任何问题欢迎跟帖讨论。
19
评论

【新手快速入门】集成环信常见问题+解决方案汇总 常见问题

dujiepeng 发表了文章 • 17966 次浏览 • 2017-05-22 15:51 • 来自相关话题

   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇
APNs证书创建和上传到环信后台头像昵称的简述和处理方案音视频离线推送Demo实现环信服务器聊天记录保存多久?离线收不到好友请求IOS中环信聊天窗口如何实现文件发送和预览的功能ios集成常见问题环信推送的一些常见问题实现名片|红包|话题聊天室等自定义cell
 
Android篇
Android sdk 的两种导入方式环信3.0SDK集成小米推送教程EaseUI库中V4、v7包冲突解决方案Android EaseUI里的百度地图替换为高德地图android扩展消息(名片集成)关于会话列表的置顶聊天java.lang.UnsatisfiedLinkError: 的问题android 端 app 后台被杀死收不到消息的解决方案
昵称头像篇
android中如何显示开发者服务器上的昵称和头像 Android中显示头像(接上一篇文章看)环信(Android)设置头像和昵称的方法(最简单暴力的基于环信demo的集成)IOS中如何显示开发者服务器上的昵称和头像【环信公开课第12期视频回放】-所有关于环信IM昵称头像的问题听这课就够了
 
直播篇
一言不合你就搞个直播APP
 
客服集成
IM-SDK和客服SDK并存开发指南—Android篇IM-SDK和客服SDK并存开发指南—iOS篇
 
开源项目
Android简版demoios简版demo凡信2.0:超仿微信的开源项目 凡信3.0:携直播和红包而来高仿微信:Github 3,515 Star方圆十里:环信编程大赛冠军项目泛聊:定一个小目标写一个QQSlack聊天机器人:一天时间做一个聊天机器人TV视频通话:在电视上视频通话视频通话:Android手机视频通话酷信:ios高仿微信公众号助手:与订阅用户聊天沟通
 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
  查看全部
   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇

 
Android篇

昵称头像篇

 
直播篇
  1. 一言不合你就搞个直播APP

 
客服集成
  1. IM-SDK和客服SDK并存开发指南—Android篇
  2. IM-SDK和客服SDK并存开发指南—iOS篇

 
开源项目

 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
 
1
回复

语音视频聊天 挂断如何有相应的响应(比如 对方已取消 iOS 端) 挂断处理 语音 视频 iOS

回复

ζ 回复了问题 • 1 人关注 • 372 次浏览 • 2018-10-23 14:27 • 来自相关话题

0
回复

视频通话的时候能否对其中的语音进行录制? 语音录制 视频通话 iOS

回复

小小聪明屋 发起了问题 • 1 人关注 • 350 次浏览 • 2018-10-18 16:32 • 来自相关话题

1
回复

OC_SDK集成参考视频 建议 环信 iOS

beyond 回复了问题 • 2 人关注 • 352 次浏览 • 2018-07-26 10:48 • 来自相关话题

4
回复

iOS获取语音信息 环信_iOS iOS

beyond 回复了问题 • 3 人关注 • 445 次浏览 • 2018-07-20 18:52 • 来自相关话题

2
回复

iOS pod集成后点击进入单聊界面崩溃 并提示libc++abi.dylib: terminating with uncaught exception of type NSException 环信 iOS

KevinGong 回复了问题 • 2 人关注 • 522 次浏览 • 2018-06-27 18:51 • 来自相关话题

0
回复

getAllIgnoredGroupIdsh获取不到屏蔽推送的群组 iOS

回复

RinoWu 发起了问题 • 1 人关注 • 405 次浏览 • 2018-06-14 16:06 • 来自相关话题

1
回复

按照您说的集成成功了,也登录成功了。但是试了下跳转单聊报错:[NSBundle initWithURL:]: nil URL argument' iOS

KevinGong 回复了问题 • 2 人关注 • 533 次浏览 • 2018-06-06 15:01 • 来自相关话题

1
回复

老哥修改EaseUI中#import <SDWebImage/UIImageView+WebCache.h>会有一下影响吧 iOS

KevinGong 回复了问题 • 2 人关注 • 605 次浏览 • 2018-06-06 15:01 • 来自相关话题

1
回复

都添加哪些库,视频里看不清啊? 环信 iOS

KevinGong 回复了问题 • 2 人关注 • 408 次浏览 • 2018-06-05 11:52 • 来自相关话题

1
评论

不是群主的好友,能否申请加入该群呢? iOS

微~凉 发表了文章 • 313 次浏览 • 2018-06-05 10:31 • 来自相关话题

不是群主的好友,能否申请加入该群呢?
不是群主的好友,能否申请加入该群呢?
2
回复

user not login iOS

差一点是帅哥 回复了问题 • 3 人关注 • 1183 次浏览 • 2018-05-09 20:56 • 来自相关话题

1
回复

Could not successfully update network info during initialization这是什么原因啊? 环信 iOS

驱马历长洲 回复了问题 • 2 人关注 • 4938 次浏览 • 2018-04-29 15:05 • 来自相关话题

1
评论

iOS 自定义cell iOS cell 自定义cell

不死小强 发表了文章 • 385 次浏览 • 2018-04-10 16:17 • 来自相关话题

简书地址:自定义cell(一)https://www.jianshu.com/p/8befabfdc4f0
               自定义cell(二)https://www.jianshu.com/p/85921a5da8d0










  查看全部
简书地址:自定义cell(一)https://www.jianshu.com/p/8befabfdc4f0
               自定义cell(二)https://www.jianshu.com/p/85921a5da8d0

QQ20180410-161412@2x.png


QQ20180410-161420@2x.png

 
1
回复

集成EaseUI发送消息成功后怎么获得发送的消息内容 iOS EaseUI

回复

磨不平的棱角。 回复了问题 • 1 人关注 • 551 次浏览 • 2018-04-09 15:14 • 来自相关话题

0
回复

集成EaseUI怎么让显示昵称而不是用户名 iOS

回复

磨不平的棱角。 发起了问题 • 1 人关注 • 657 次浏览 • 2018-03-31 09:13 • 来自相关话题

0
回复

iOS 提示“缩略图获取失败” 缩略图 iOS

回复

磨不平的棱角。 发起了问题 • 1 人关注 • 520 次浏览 • 2018-03-29 10:04 • 来自相关话题

0
回复

环信更新证书问题 iOS 证书

回复

微~凉 发起了问题 • 1 人关注 • 586 次浏览 • 2018-03-27 16:53 • 来自相关话题

0
回复

环信创建群 iOS iOS 环信 iOS 好友 环信 ios 环信_iOS

回复

未完成 发起了问题 • 1 人关注 • 732 次浏览 • 2018-02-13 16:13 • 来自相关话题

0
回复

643dc0e4d22c2079f10522168b4ed470求助 iOS

回复

雨夜黑瞳 发起了问题 • 1 人关注 • 626 次浏览 • 2018-02-09 12:57 • 来自相关话题

0
回复

i环信发送文件 消息, 自动存储嘛? iOS 环信_iOS

回复

Joshua1 发起了问题 • 1 人关注 • 2848 次浏览 • 2018-01-30 19:51 • 来自相关话题

0
回复

iOS登录报错203? iOS 登录报错 登录203

回复

shallwe 发起了问题 • 1 人关注 • 660 次浏览 • 2018-01-30 17:24 • 来自相关话题

1
回复

iOS端,发送消息时调用的方法内部有关于插入信息到数据库吗? iOS

回复

chickenho 回复了问题 • 1 人关注 • 749 次浏览 • 2018-01-02 20:12 • 来自相关话题

条新动态, 点击查看
zl

zl 回答了问题 • 2015-12-03 17:00 • 19 个回复 不感兴趣

iOS EaseMessageCell.h:48:44: Property has a previous declaration

赞同来自:

emoji的  表情。添加下相关库、
emoji的  表情。添加下相关库、
获取会话的最后一条消息,conversation.latestmessage.ext 然后获取出来之后去显示吧
 
获取会话的最后一条消息,conversation.latestmessage.ext 然后获取出来之后去显示吧
 
发送方看下didsendmessage回调方法中,message.ext有没有设置的值。
发送方看下didsendmessage回调方法中,message.ext有没有设置的值。
zl

zl 回答了问题 • 2016-02-22 17:46 • 4 个回复 不感兴趣

好友申请和入群申请

赞同来自:

是啊 。这个是回调,代理传值。sdk内部接收到好友请求,然后返回给你,就是通过这个方法接收。
是啊 。这个是回调,代理传值。sdk内部接收到好友请求,然后返回给你,就是通过这个方法接收。
sdk.a包括实时语音通话和实时视频,里面有lite.a里的功能。 lite.a有基本聊天功能。 这两个留一个就行了。不用实时语音通话和视频,就用lite.a就可以了
sdk.a包括实时语音通话和实时视频,里面有lite.a里的功能。 lite.a有基本聊天功能。 这两个留一个就行了。不用实时语音通话和视频,就用lite.a就可以了
zl

zl 回答了问题 • 2016-03-23 11:53 • 3 个回复 不感兴趣

与支付宝冲突

赞同来自:

other linker 设置下 -force_load + 支付宝的库的路径,并放在最前面。这里不需要再设置环信sdk。
other linker 设置下 -force_load + 支付宝的库的路径,并放在最前面。这里不需要再设置环信sdk。
环信沈冲

环信沈冲 回答了问题 • 2016-04-07 18:52 • 1 个回复 不感兴趣

iOS集成环信Demo 3.0在不同机型上运行问题

赞同来自:

在3.0demo中找到FixFopen.c导入项目中,并在pch文件中的首尾加上__OBJC__和endif
在3.0demo中找到FixFopen.c导入项目中,并在pch文件中的首尾加上__OBJC__和endif
donghai

donghai 回答了问题 • 2016-04-12 19:37 • 25 个回复 不感兴趣

求一份 iOS 3.x 群聊demo

赞同来自:

QQ邮箱给我,我给你发一份。
QQ邮箱给我,我给你发一份。
貌似应该是三方框架有重复, 解决了
貌似应该是三方框架有重复, 解决了
- (NSMutableArray *)loadDataSource

{

    NSMutableArray *ret = nil;

    NSArray *conversations = [[EaseMob sharedInstance].chat... 显示全部 »
- (NSMutableArray *)loadDataSource

{

    NSMutableArray *ret = nil;

    NSArray *conversations = [[EaseMob sharedInstance].chatManager conversations];




    NSArray* sorte = [conversations sortedArrayUsingComparator:

           ^(EMConversation *obj1, EMConversation* obj2){

               EMMessage *message1 = [obj1 latestMessage];

               EMMessage *message2 = [obj2 latestMessage];

               if(message1.timestamp > message2.timestamp) {

                   return(NSComparisonResult)NSOrderedAscending;

               }else {

                   return(NSComparisonResult)NSOrderedDescending;

               }

           }];

    

    ret = [[NSMutableArray alloc] initWithArray:sorte];

    return ret;

}
 
你在每次刷新的时候调这个方法就可以吧。  Demo有
lizilong

lizilong 回答了问题 • 2016-05-16 14:26 • 2 个回复 不感兴趣

iOS集成parse的报错,大神请进

赞同来自:

应该是缺少这两个依赖
Accounts.framework
Social.framework
 
应该是缺少这两个依赖
Accounts.framework
Social.framework
 
zhangyb

zhangyb 回答了问题 • 2016-06-20 19:37 • 1 个回复 不感兴趣

ios 8, ipV6, 环信demo,无法登录,无法用了

赞同来自:

iOS9.2以下,不支持ipv6
iOS9.2以下,不支持ipv6
feynmanren

feynmanren 回答了问题 • 2016-06-29 15:16 • 1 个回复 不感兴趣

环信3.0,登录admin帐户失败!

赞同来自:

自己回答吧, 需要自己手动创建admin账户。
自己回答吧, 需要自己手动创建admin账户。
mazhihua

mazhihua 回答了问题 • 2016-08-09 19:28 • 2 个回复 不感兴趣

iOS 角标问题 app 活跃状态 和 被杀死 情况下

赞同来自:

您好,这个角标是服务器设置的,改不了的,因为服务器推送的离线消息是从0 开始计算的,所以从1 开始重新累加了
您好,这个角标是服务器设置的,改不了的,因为服务器推送的离线消息是从0 开始计算的,所以从1 开始重新累加了
[[EaseSDKHelper shareHelper] hyphenateApplication:application
                    didFinishLaunchingWithOptions:launchOptions
    ... 显示全部 »
[[EaseSDKHelper shareHelper] hyphenateApplication:application
                    didFinishLaunchingWithOptions:launchOptions
                                           appkey:appkey
                                     apnsCertName:apnsCertName
                                      otherConfig:@{kSDKConfigEnableConsoleLogger:[NSNumber numberWithBool:YES]}];
Swift

Swift 回答了问题 • 2016-11-03 16:46 • 2 个回复 不感兴趣

ios如何添加Emoji哇

赞同来自:

已解决EaseMessageViewController在的viewdidload方法的
第一行添加:[self setupEmotion];
最后一行添加: EaseEmotionManager *manager= [[EaseEmotionManager ... 显示全部 »
已解决EaseMessageViewController在的viewdidload方法的
第一行添加:[self setupEmotion];
最后一行添加: EaseEmotionManager *manager= [[EaseEmotionManager alloc] initWithType:EMEmotionDefault emotionRow:3 emotionCol:7 emotions:[EaseEmoji allEmoji]]; [self.faceView setEmotionManagers:@[manager]];
然后神奇的出现了emoji
环信沈冲

环信沈冲 回答了问题 • 2016-12-26 14:32 • 2 个回复 不感兴趣

环信服务端集成,如何集成?

赞同来自:

客户端直接集成SDK即可,服务端可以根据自己需求调用相应的rest接口集成
客户端直接集成SDK即可,服务端可以根据自己需求调用相应的rest接口集成
beyond

beyond 回答了问题 • 2017-01-10 17:32 • 1 个回复 不感兴趣

iOS动态库sdk上架问题?

赞同来自:

你好,环信在V3.2.3 2016-12-29版本支持了动态SDK
可以参考环信动态库sdk上架问题解决方案 http://www.imgeek.org/article/825308639
你好,环信在V3.2.3 2016-12-29版本支持了动态SDK
可以参考环信动态库sdk上架问题解决方案 http://www.imgeek.org/article/825308639
donghai

donghai 回答了问题 • 2017-01-21 11:49 • 6 个回复 不感兴趣

关于ios登陆的问题

赞同来自:

环信demo在初始化SDK方法那里换你自己的appkey,别在宏定义那里换,报用户不存在就是你自己的appkey填的不对,或者登录环信的账号不对
环信demo在初始化SDK方法那里换你自己的appkey,别在宏定义那里换,报用户不存在就是你自己的appkey填的不对,或者登录环信的账号不对
环信沈冲

环信沈冲 回答了问题 • 2017-02-15 12:14 • 1 个回复 不感兴趣

iOS 是从哪一版开始不支持 iOS 7的?

赞同来自:

3.2.3动态库版本不支持iOS7
3.2.3动态库版本不支持iOS7
 插入消息方法换一下 [[EMClient sharedClient].chatManager importMessages:@[message1] completion:^(EMError *aError) {

                    }]... 显示全部 »
 插入消息方法换一下 [[EMClient sharedClient].chatManager importMessages:@[message1] completion:^(EMError *aError) {

                    }];换成 [conversation insertmessage:]
这个 问题很可能就是 导入的环信依赖库重复了, 即导入了包含实时音视频的依赖库,又导入了不包含实时音视频的依赖库。 可以检查一下,导入的依赖库是否导入错了, 重新导入,就可以解决这个问题了。
这个 问题很可能就是 导入的环信依赖库重复了, 即导入了包含实时音视频的依赖库,又导入了不包含实时音视频的依赖库。 可以检查一下,导入的依赖库是否导入错了, 重新导入,就可以解决这个问题了。
donghai

donghai 回答了问题 • 2017-03-31 18:10 • 1 个回复 不感兴趣

ios 环信最新版本 打包上线时候报错

赞同来自:

环信动态库分离上架:http://www.jianshu.com/p/f058b25163b8  看下这里
环信动态库分离上架:http://www.jianshu.com/p/f058b25163b8  看下这里
zl

zl 回答了问题 • 2017-04-20 15:21 • 2 个回复 不感兴趣

EMConversation中的ext保存后读取不出来

赞同来自:

具体操作代码贴一下。
具体操作代码贴一下。
wangyuzhang

wangyuzhang 回答了问题 • 2017-08-30 21:09 • 1 个回复 不感兴趣

环信 手动添加消息实现逻辑?

赞同来自:

发送消息的入口,需要根据你们的业务来决定,在哪里触发此消息的发送。
发送的消息创建时,EMMessage初始化参数 conversationId和to传入接收方的id即可,然后通过chatManager的接口来发送创建好的消息
- (void)sendMess... 显示全部 »
发送消息的入口,需要根据你们的业务来决定,在哪里触发此消息的发送。
发送的消息创建时,EMMessage初始化参数 conversationId和to传入接收方的id即可,然后通过chatManager的接口来发送创建好的消息
- (void)sendMessage:(EMMessage *)aMessage
           progress:(void (^)(int progress))aProgressBlock
         completion:(void (^)(EMMessage *message, EMError *error))aCompletionBlock;
0
评论

NIUDAY 11.23 北京站 |环信CEO刘俊彦现场讲述人工智能赋能客户互动之现状与未来 刘俊彦 人工智能 环信 七牛云

beyond 发表了文章 • 50 次浏览 • 2018-11-15 16:36 • 来自相关话题

2018 年是见证「奇迹」的一年。AI 从多年的热门话题中开始走下神坛,逐渐深入到了各个行业,加速经济结构优化及行业智慧化升级,AI 已不再是难以企及的神话而是可触摸的美好未来。 政策支持加上资本推动,无论是从新兴行业还是传统行业都出现了人工智能方面的布局者和佼佼者,智慧教育、移动社交、智能语音、智慧客服、传统媒体等行业都在突破技术上和流程上的难点和困惑。

11 月 23 日,一场由七牛云主办主题为「AI 产业技术的渗透与融合」的 NIUDAY 小牛汇共享日将在北京举行。会上,将邀请众多行业内知名企业及技术专家,针对当前 AI 在技术上以及行业中遇到的一些壁垒问题,进行深入探讨和分享。

智慧教育、移动未来,体验指尖上的 AI

在此次活动中,除行业专家的视点剖析之外,对于 AI 与行业的渗透与融合度是理论大于实践亦或是理论与实践已完美结合,已经应用的行业代表案例有哪些?对于这些问题,大家可以在此次活动中得以解惑。




刘俊彦
手机电商、手机软件系统的快速更迭让在线客服、移动客服有了更大的发展空间,成了市场中的一片蓝海,究竟现状如何、客户体验的真实反馈是什么?环信 CEO 刘俊彦先生带来的《人工智能赋能客户互动之现状与未来》让你更贴近生活,更能体会 AI 的无处不在。




谢华亮

科技发展教育先行,技术与教育的结合又将碰撞出怎样的火花,会上,将有好未来 SEG 智慧教育事业部技术总监谢华亮带来主题为《AI 在教育行业中的应用》的精彩演讲。




佘超杰

移动互联网的发展已影响到我们身边的每一个人,游戏、娱乐、社交等手机平台的火爆更是带来了巨大的信息流量。但如何应对这些突然爆发的信息流,如何让产品与技术更好地去结合以吸引更多的受众人群,成为人们关注的话题。以作为专注于移动互联网社交的知名企业 Blued 为例,实践与技术并重的技术专家佘超杰将分享给我们《AI 在 Blued 上的应用》。
                
连线行业专家,洞悉专业视点

人工智能技术在飞速发展过程中得到了国家以及政府的极大关注与大力支持,中国人工智能产业发展联盟作为国家发展改革委、科学技术部、工业和信息化部、中央网信办四部委共同指导下的人工智能产业权威联盟机构,也将加入到此次 NIUDAY 小牛汇共享日活动中来。





孙明俊

中国信息通信研究院人工智能部副主任,中国人工智能产业发展联盟总体组组长、数据中心联盟秘书长孙明俊女士将出席本次活动,并将从行业专家的角度带来主题为《人工智能产业发展水平分析》的演讲,向大家解析产业发展水平、分享行业利好政策等。

战略签约中国网 传统与创新的再突破





杨新华

值得一提的是,本次 NIUDAY 小牛汇共享日活动上七牛云将与中国互联网新闻中心举行战略合作签约仪式。届时,中国互联网新闻中心·中国网副总编辑杨新华先生将带来《AI 如何讲好中国故事》的演讲,介绍七牛云在未来媒体平台构建中发挥的作用,以及正处于变革中的媒体行业对 AI 的思考。

当然作为本次 NIUDAY 小牛汇活动的主办方,七牛云也准备了满满的干货带给大家。七牛云技术总监陈超《数据智能时代的智慧工厂实践》、七牛云人工智能实验室资深产品经理杨叶青《一站式审核助力无忧运营》带你体会七牛云两大重要产品线的技术与发展。

更多大咖嘉宾,请往下看~ 





关于环信机器人:

环信机器人已经在中通快递、新东方、天津农商行、宜家、环球捕手等头部企业的“双十一”中发挥着中流砥柱的作用,机器人可以帮助解决80%以上的常见问题,在售后环节能替代超90%的人工,轻松实现7*24无间断客户服务,AI从此让我们的客服人员告别苦逼,云淡风轻,笑对各种节假日促销、狂欢节。

目前,环信机器人已经广泛服务于包括保险、证券、银行、教育、物流、商旅、电商、汽车等行业的数万客户,日机器人会话超千万条。
活动报名地址:http://www.huodongxing.com/event/2464773427600 查看全部

2018 年是见证「奇迹」的一年。AI 从多年的热门话题中开始走下神坛,逐渐深入到了各个行业,加速经济结构优化及行业智慧化升级,AI 已不再是难以企及的神话而是可触摸的美好未来。 政策支持加上资本推动,无论是从新兴行业还是传统行业都出现了人工智能方面的布局者和佼佼者,智慧教育、移动社交、智能语音、智慧客服、传统媒体等行业都在突破技术上和流程上的难点和困惑。



11 月 23 日,一场由七牛云主办主题为「AI 产业技术的渗透与融合」的 NIUDAY 小牛汇共享日将在北京举行。会上,将邀请众多行业内知名企业及技术专家,针对当前 AI 在技术上以及行业中遇到的一些壁垒问题,进行深入探讨和分享。

智慧教育、移动未来,体验指尖上的 AI

在此次活动中,除行业专家的视点剖析之外,对于 AI 与行业的渗透与融合度是理论大于实践亦或是理论与实践已完美结合,已经应用的行业代表案例有哪些?对于这些问题,大家可以在此次活动中得以解惑。

微信图片_20181115162453.jpg

刘俊彦


手机电商、手机软件系统的快速更迭让在线客服、移动客服有了更大的发展空间,成了市场中的一片蓝海,究竟现状如何、客户体验的真实反馈是什么?环信 CEO 刘俊彦先生带来的《人工智能赋能客户互动之现状与未来》让你更贴近生活,更能体会 AI 的无处不在。

微信图片_20181115162531.jpg

谢华亮



科技发展教育先行,技术与教育的结合又将碰撞出怎样的火花,会上,将有好未来 SEG 智慧教育事业部技术总监谢华亮带来主题为《AI 在教育行业中的应用》的精彩演讲。

微信图片_20181115162544.jpg

佘超杰



移动互联网的发展已影响到我们身边的每一个人,游戏、娱乐、社交等手机平台的火爆更是带来了巨大的信息流量。但如何应对这些突然爆发的信息流,如何让产品与技术更好地去结合以吸引更多的受众人群,成为人们关注的话题。以作为专注于移动互联网社交的知名企业 Blued 为例,实践与技术并重的技术专家佘超杰将分享给我们《AI 在 Blued 上的应用》。
                
连线行业专家,洞悉专业视点

人工智能技术在飞速发展过程中得到了国家以及政府的极大关注与大力支持,中国人工智能产业发展联盟作为国家发展改革委、科学技术部、工业和信息化部、中央网信办四部委共同指导下的人工智能产业权威联盟机构,也将加入到此次 NIUDAY 小牛汇共享日活动中来。

微信图片_20181115162600.jpg


孙明俊



中国信息通信研究院人工智能部副主任,中国人工智能产业发展联盟总体组组长、数据中心联盟秘书长孙明俊女士将出席本次活动,并将从行业专家的角度带来主题为《人工智能产业发展水平分析》的演讲,向大家解析产业发展水平、分享行业利好政策等。

战略签约中国网 传统与创新的再突破

微信图片_20181115162615.jpg


杨新华



值得一提的是,本次 NIUDAY 小牛汇共享日活动上七牛云将与中国互联网新闻中心举行战略合作签约仪式。届时,中国互联网新闻中心·中国网副总编辑杨新华先生将带来《AI 如何讲好中国故事》的演讲,介绍七牛云在未来媒体平台构建中发挥的作用,以及正处于变革中的媒体行业对 AI 的思考。

当然作为本次 NIUDAY 小牛汇活动的主办方,七牛云也准备了满满的干货带给大家。七牛云技术总监陈超《数据智能时代的智慧工厂实践》、七牛云人工智能实验室资深产品经理杨叶青《一站式审核助力无忧运营》带你体会七牛云两大重要产品线的技术与发展。

更多大咖嘉宾,请往下看~ 

5bed2dcd27fe6.jpg

关于环信机器人:

环信机器人已经在中通快递、新东方、天津农商行、宜家、环球捕手等头部企业的“双十一”中发挥着中流砥柱的作用,机器人可以帮助解决80%以上的常见问题,在售后环节能替代超90%的人工,轻松实现7*24无间断客户服务,AI从此让我们的客服人员告别苦逼,云淡风轻,笑对各种节假日促销、狂欢节。

目前,环信机器人已经广泛服务于包括保险、证券、银行、教育、物流、商旅、电商、汽车等行业的数万客户,日机器人会话超千万条。
活动报名地址:http://www.huodongxing.com/event/2464773427600
4
评论

【开源项目】全国首个开源直播小程序源码 环信公开课 小程序 直播

beyond 发表了文章 • 3066 次浏览 • 2018-07-20 17:30 • 来自相关话题

今天你看直播了吗?拥有10亿微信生态用户的小程序已经成为了继移动互联后的又一个现象级风口,随着微信小程序对外开放实时音视频录制及播放等更多连接能力,小程序与直播强强联合,在各行各业找到了非常多的玩法,小程序直播相比微信直播和APP直播更加简洁、流畅、低延时、多入口等众多优势迅速向商业直播领域及泛娱乐直播领域蔓延。从小游戏、内容付费、工具、大数据、社交电商创业者到传统品牌商们,都在努力搭上小程序直播这辆快车,以免错过微信生态里新的流量洼地。
 





作为一名环信生态圈资深开发者,本着对技术的热衷,对环信的眷恋和对党的忠诚,基于环信即时通讯云写了“直播购物小程序”,目前项目源码已全部免费开放,希望对有需求的企业和开发者提供一个思路和参考。
直播购物小程序源码github地址:https://github.com/YuTongNetworkTechnology/wechat_live/tree/master 
git打不开可直接点下面链接下载


小程序直播demo_2018-06-21.zip







直播购物小程序运行预览图 
 
小程序体验指南(仅需两步):
 
1、下载微信小程序开发工具,下载地址:https://developers.weixin.qq.c ... .html 
 




2、导入源码:将附件的源码解压直接导入 







环信小程序直播技术文档
一、 使用的技术
1、 环信IM直播室。
2、 微信小程序实时音视频播放组件live-player。
3、 推流软件(obs、易推流)等推流。
4、 视频流服务器(UCLOUD、七牛、腾讯)等视频流服务器。
二、 系统使用流程。
1、 视频推流软件将视频流推到流服务器。
2、 打开视频直播demo小程序注册环信账号。
3、 进入软件直播室进行测试。
三、 技术流程及使用的SDk
1、 注册环信账号
打开https://www.easemob.com/ 环信官网,点击右上角注册按钮,选择[注册即时通讯云]




填写对相关信息进行注册





注册成功后进行登录




注:新注册用户需进行账号的认证。
2、 直播应用创建
登录成功点击应用列表选择创建应用




输入应用名称等信息
 





创建成功后点击应用进入





需要注意的是应用的OrgName 和AppName这两个是以后都需要用到的两个参数变量




3、 直播创建
1)在创建直播之前需要对应用进行设置首先需要设置应用的直播流地址
第一步获取应用管理员的Tokencurl -X POST "https://a1.easemob.com/[应用OrgName]/[应用AppName]/token" -d '{"grant_type":"client_credentials","client_id":"[应用client_id]","client_secret":"[应用] client_secret"}'返回格式{
"access_token":"YWMtWY779DgJEeS2h9OR7fw4QgAAAUmO4Qukwd9cfJSpkWHiOa7MCSk0MrkVIco",
"expires_in":5184000,
"application":"c03b3e30-046a-11e4-8ed1-5701cdaaa0e4"












第二步设置直播流地址curl -X POST -H "Authorization: Bearer [管理员Token]" " https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms/stream_url -d '{"pc_pull":"[pc拉流地址]","pc_push":"[pc推流地址]","mobile_pull":"[手机拉流地址]","mobile_push":"[手机推流地址]"}'"成功返回格式:{
"action": "post",
"application": "e1a09de0-0e03-11e7-ad8e-a1d913615409",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"pc_pull": true,
"mobile_push": true,
"mobile_pull": true,
"pc_push": true
},
"timestamp": 1494084474885,
"duration": 1,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












2)创建主播
点击IM用户





点击注册IM用户





填写用户信息





创建用户的过程同样也可以通过REST API形式进行curl -X POST -i " https://a1.easemob.com/[应用OrgName]/[应用AppName]/users" -d '{"username":"[用户名]","password":"[密码]"}'
注:应用必须为开放注册





将注册的用户添加为主播curl -X POST -H "Authorization: [管理员Token]" https://a1.easemob.com/[应用OrgName]/[应用AppName]/super_admin -d'{"superadmin":"[IM用户名]"}'返回结果示例:{
"action": "post",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"result": "success"
},
"timestamp": 1496236798886,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












3)创建直播
点击直播





点击新建房间





填写房间信息




创建房间同时也可以使用REST API形式进行详情可以查看http://docs.easemob.com/im/live/server-integration环信官方文档。
4、 小程序demo集成使用
小程序直播购物demo集成官方WebIM SDK详情请查看https://github.com/easemob/webim-weixin-xcx
Demo具体配置如下
打开demo 下sdk配置文件





修改appkey为自己应用的appkey





打开pages/live/index.js修改房间默认拉流地址及直播间房间号





四、 扩展说明
Demo中房间为固定测试房间,实际使用中应获取环信直播的房间信息及房间列表。具体如下:
获取直播间列表:curl -X GET -H "Authorization: Bearer [用户Token]" https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms?ongoing=true&limit=[获取数量]&cursor=[游标地址(不填写为充开始查询)]
响应:{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"params": {
"cursor": [
"ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6MzE"
],
"ongoing": [
"true"
],
"limit": [
"2"
]
},
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": [
{
"id": "1924",
"chatroom_id": "17177265635330",
"title": "具体了",
"desc": "就咯",
"startTime": 1495779917352,
"endTime": 1495779917352,
"anchor": "wuls",
"gift_count": 0,
"praise_count": 0,
"current_user_count": 8,
"max_user_count": 9,
"status": "ongoing",
"cover_picture_url": "",
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1"
},
{
"id": "1922",
"chatroom_id": "17175003856897",
"title": "香山",
"desc": "随便",
"startTime": 1495777760957,
"endTime": 1495777760957,
"anchor": "sx001",
"gift_count": 0,
"praise_count": 8,
"current_user_count": 1,
"max_user_count": 3,
"status": "ongoing",
"cover_picture_url": "http://127.0.0.1:8080/easemob- ... ot%3B,
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1"
}
],
"timestamp": 1496303336669,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"cursor": "ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6NDk",
"count": 2
}












获取直播间详情:curl -X GET -H "Authorization: Bearer [用户Token]" " https://a1.easemob.com/[应用OrgName]/[应用AppName]/[房间id]/status"响应:{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"liveRoomID": "1946",
"status": "ongoing"
},
"timestamp": 1496234759930,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"count": 0
}














 
使用环信直播购物小程序遇到任何问题欢迎跟帖讨论。 查看全部
今天你看直播了吗?
拥有10亿微信生态用户的小程序已经成为了继移动互联后的又一个现象级风口,随着微信小程序对外开放实时音视频录制及播放等更多连接能力,小程序与直播强强联合,在各行各业找到了非常多的玩法,小程序直播相比微信直播和APP直播更加简洁、流畅、低延时、多入口等众多优势迅速向商业直播领域及泛娱乐直播领域蔓延。从小游戏、内容付费、工具、大数据、社交电商创业者到传统品牌商们,都在努力搭上小程序直播这辆快车,以免错过微信生态里新的流量洼地。
 
微信图片_20180725162426.jpg


作为一名环信生态圈资深开发者,本着对技术的热衷,对环信的眷恋和对党的忠诚,基于环信即时通讯云写了“直播购物小程序”,目前项目源码已全部免费开放,希望对有需求的企业和开发者提供一个思路和参考。
直播购物小程序源码github地址:https://github.com/YuTongNetworkTechnology/wechat_live/tree/master 
git打不开可直接点下面链接下载



预览图.jpg

直播购物小程序运行预览图 
 
小程序体验指南(仅需两步):
 
1、下载微信小程序开发工具,下载地址:https://developers.weixin.qq.c ... .html 
 
Catch9A07(07-20-17-38-30).jpg

2、导入源码:将附件的源码解压直接导入 


Catch1C69(07-20-17-38-30).jpg


环信小程序直播技术文档
一、 使用的技术
1、 环信IM直播室。
2、 微信小程序实时音视频播放组件live-player。
3、 推流软件(obs、易推流)等推流。
4、 视频流服务器(UCLOUD、七牛、腾讯)等视频流服务器。
二、 系统使用流程。
1、 视频推流软件将视频流推到流服务器。
2、 打开视频直播demo小程序注册环信账号。
3、 进入软件直播室进行测试。
三、 技术流程及使用的SDk
1、 注册环信账号
打开https://www.easemob.com/ 环信官网,点击右上角注册按钮,选择[注册即时通讯云]
1.png

填写对相关信息进行注册

2.png

注册成功后进行登录
3.png

注:新注册用户需进行账号的认证。
2、 直播应用创建
登录成功点击应用列表选择创建应用
4.png

输入应用名称等信息
 

5.png

创建成功后点击应用进入

6.png

需要注意的是应用的OrgName 和AppName这两个是以后都需要用到的两个参数变量
7.png

3、 直播创建
1)在创建直播之前需要对应用进行设置首先需要设置应用的直播流地址
第一步获取应用管理员的Token
curl -X POST "https://a1.easemob.com/[应用OrgName]/[应用AppName]/token" -d '{"grant_type":"client_credentials","client_id":"[应用client_id]","client_secret":"[应用] client_secret"}'
返回格式
{
"access_token":"YWMtWY779DgJEeS2h9OR7fw4QgAAAUmO4Qukwd9cfJSpkWHiOa7MCSk0MrkVIco",
"expires_in":5184000,
"application":"c03b3e30-046a-11e4-8ed1-5701cdaaa0e4"












第二步设置直播流地址
curl -X POST -H "Authorization: Bearer [管理员Token]"  " https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms/stream_url -d '{"pc_pull":"[pc拉流地址]","pc_push":"[pc推流地址]","mobile_pull":"[手机拉流地址]","mobile_push":"[手机推流地址]"}'"
成功返回格式:
{
"action": "post",
"application": "e1a09de0-0e03-11e7-ad8e-a1d913615409",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"pc_pull": true,
"mobile_push": true,
"mobile_pull": true,
"pc_push": true
},
"timestamp": 1494084474885,
"duration": 1,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












2)创建主播
点击IM用户

8.png

点击注册IM用户

9.png

填写用户信息

10.png

创建用户的过程同样也可以通过REST API形式进行
curl -X POST -i " https://a1.easemob.com/[应用OrgName]/[应用AppName]/users" -d '{"username":"[用户名]","password":"[密码]"}'

注:应用必须为开放注册

11.png

将注册的用户添加为主播
curl -X POST -H "Authorization: [管理员Token]"  https://a1.easemob.com/[应用OrgName]/[应用AppName]/super_admin -d'{"superadmin":"[IM用户名]"}'
返回结果示例:
{
"action": "post",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"result": "success"
},
"timestamp": 1496236798886,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












3)创建直播
点击直播

12.png

点击新建房间

13.png

填写房间信息
14.png

创建房间同时也可以使用REST API形式进行详情可以查看http://docs.easemob.com/im/live/server-integration环信官方文档。
4、 小程序demo集成使用
小程序直播购物demo集成官方WebIM SDK详情请查看https://github.com/easemob/webim-weixin-xcx
Demo具体配置如下
打开demo 下sdk配置文件

15.png

修改appkey为自己应用的appkey

16.png

打开pages/live/index.js修改房间默认拉流地址及直播间房间号

17.png

四、 扩展说明
Demo中房间为固定测试房间,实际使用中应获取环信直播的房间信息及房间列表。具体如下:
获取直播间列表:
curl -X GET -H "Authorization: Bearer  [用户Token]"  https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms?ongoing=true&limit=[获取数量]&cursor=[游标地址(不填写为充开始查询)]

响应:
{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"params": {
"cursor": [
"ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6MzE"
],
"ongoing": [
"true"
],
"limit": [
"2"
]
},
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": [
{
"id": "1924",
"chatroom_id": "17177265635330",
"title": "具体了",
"desc": "就咯",
"startTime": 1495779917352,
"endTime": 1495779917352,
"anchor": "wuls",
"gift_count": 0,
"praise_count": 0,
"current_user_count": 8,
"max_user_count": 9,
"status": "ongoing",
"cover_picture_url": "",
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1"
},
{
"id": "1922",
"chatroom_id": "17175003856897",
"title": "香山",
"desc": "随便",
"startTime": 1495777760957,
"endTime": 1495777760957,
"anchor": "sx001",
"gift_count": 0,
"praise_count": 8,
"current_user_count": 1,
"max_user_count": 3,
"status": "ongoing",
"cover_picture_url": "http://127.0.0.1:8080/easemob- ... ot%3B,
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1"
}
],
"timestamp": 1496303336669,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"cursor": "ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6NDk",
"count": 2
}












获取直播间详情:
curl -X GET -H "Authorization: Bearer [用户Token]" " https://a1.easemob.com/[应用OrgName]/[应用AppName]/[房间id]/status"
响应:
{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"liveRoomID": "1946",
"status": "ongoing"
},
"timestamp": 1496234759930,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"count": 0
}














 
使用环信直播购物小程序遇到任何问题欢迎跟帖讨论。
19
评论

【新手快速入门】集成环信常见问题+解决方案汇总 常见问题

dujiepeng 发表了文章 • 17966 次浏览 • 2017-05-22 15:51 • 来自相关话题

   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇
APNs证书创建和上传到环信后台头像昵称的简述和处理方案音视频离线推送Demo实现环信服务器聊天记录保存多久?离线收不到好友请求IOS中环信聊天窗口如何实现文件发送和预览的功能ios集成常见问题环信推送的一些常见问题实现名片|红包|话题聊天室等自定义cell
 
Android篇
Android sdk 的两种导入方式环信3.0SDK集成小米推送教程EaseUI库中V4、v7包冲突解决方案Android EaseUI里的百度地图替换为高德地图android扩展消息(名片集成)关于会话列表的置顶聊天java.lang.UnsatisfiedLinkError: 的问题android 端 app 后台被杀死收不到消息的解决方案
昵称头像篇
android中如何显示开发者服务器上的昵称和头像 Android中显示头像(接上一篇文章看)环信(Android)设置头像和昵称的方法(最简单暴力的基于环信demo的集成)IOS中如何显示开发者服务器上的昵称和头像【环信公开课第12期视频回放】-所有关于环信IM昵称头像的问题听这课就够了
 
直播篇
一言不合你就搞个直播APP
 
客服集成
IM-SDK和客服SDK并存开发指南—Android篇IM-SDK和客服SDK并存开发指南—iOS篇
 
开源项目
Android简版demoios简版demo凡信2.0:超仿微信的开源项目 凡信3.0:携直播和红包而来高仿微信:Github 3,515 Star方圆十里:环信编程大赛冠军项目泛聊:定一个小目标写一个QQSlack聊天机器人:一天时间做一个聊天机器人TV视频通话:在电视上视频通话视频通话:Android手机视频通话酷信:ios高仿微信公众号助手:与订阅用户聊天沟通
 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
  查看全部
   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇

 
Android篇

昵称头像篇

 
直播篇
  1. 一言不合你就搞个直播APP

 
客服集成
  1. IM-SDK和客服SDK并存开发指南—Android篇
  2. IM-SDK和客服SDK并存开发指南—iOS篇

 
开源项目

 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
 
10
回复

收集基于环信SDK开发的开源项目 开源项目

Tayler 回复了问题 • 13 人关注 • 11306 次浏览 • 2018-10-30 17:59 • 来自相关话题

2
评论

ios详细的证书创建和上传 iOS ios证书

o孟力o 发表了文章 • 10030 次浏览 • 2015-12-30 11:58 • 来自相关话题

为新手制定一个详细的创建推送证书流程
第一步:生成.certSigningRequest证书
打开钥匙串,在钥匙串导航栏找到,“钥匙串访问”这一项,在钥匙串访问找到“从证书颁发机构请求证书”这一项,如图




点击后进入到如图界面




用户电子邮件地址:写上你的邮件就行
常用名称:随便写
然后选择储存到磁盘,CA电子邮件地址不用写,点继续就生成了.certSigningRequest文件了,记得这个文件的储存位置,一会我们生成推送证书还要用到
第二步:创建推送证书
打开开发者中心,进入生成证书界面(应该找的到吧),进入之后,如果你要生成测试环境的推送证书,选择Certificates-Development,如果你要生成正式环境证书就选择Certificates-Priduction,这里我们以测试为例,点击右上角加号,如图




进入下个界面,如果你要生成测试环境推送证书,就选择红色部分,你要生成正式环境证书,就选择绿色部分,如图




点Continue,进入下个界面,让选择app id,提醒一下,选择app id要和你工程的Bundle Identifier对应,新手应该找的到Bundle Identifier吧,找不到看下面的图,我截了张图




选择完app id ,一直点击Continue,进入如图界面




让选择文件,还记得我们刚开始用钥匙串生成的.certSigningRequest在哪放着吧,就选择它就对了,选择完之后,接着往下面走,把证书下载下来之后,双击就放到钥匙串里面了,这时候推送证书就生成完了
第三步:生成p12文件
我们最终放到环信后台的证书是p12证书,打开钥匙串,找到我们刚才生成的证书,右键找到导出选项,如图




点击导出“Apple Development.....”之后进入下个界面,如图:




红色圈着的部分,储存为:里面要写上证书的名字,名字随便起,不过要记住这个名字,一会在环信后台上传证书还要用到这个名字,名字填完之后,点击储存,储存的位置要记住,一会还要用这个文件,这个文件就是我们要上传到环信的后台的p12文件,(储存的时候会让你输入密码,这个密码你要记住,一会上传到环信后台证书的时候要用到这个密码),这时候p12文件就制作完成了。
第四步:把p12文件上传到环信后台
打开我们的环信后台,找到 推送证书-ios,下面有上传证书的地方,如图




证书名称:就是我们刚才生成p12的名称,我的叫zhuma_test
证书:就是我们刚才生成的p12证书,找到传上去
证书密码:生成p12证书的时候输入的密码(不是瞎输的吧,能记得吗,记不得重新导p12文件吧)
证书类型:我生成的是开发环境的证书,就勾选开发环境
填完之后就可以上传了,上传成功,就ok了 查看全部


为新手制定一个详细的创建推送证书流程
第一步:生成.certSigningRequest证书
打开钥匙串,在钥匙串导航栏找到,“钥匙串访问”这一项,在钥匙串访问找到“从证书颁发机构请求证书”这一项,如图
p1.png

点击后进入到如图界面
p2.png

用户电子邮件地址:写上你的邮件就行
常用名称:随便写
然后选择储存到磁盘,CA电子邮件地址不用写,点继续就生成了.certSigningRequest文件了,记得这个文件的储存位置,一会我们生成推送证书还要用到
第二步:创建推送证书
打开开发者中心,进入生成证书界面(应该找的到吧),进入之后,如果你要生成测试环境的推送证书,选择Certificates-Development,如果你要生成正式环境证书就选择Certificates-Priduction,这里我们以测试为例,点击右上角加号,如图
p3.png

进入下个界面,如果你要生成测试环境推送证书,就选择红色部分,你要生成正式环境证书,就选择绿色部分,如图
p4.png

点Continue,进入下个界面,让选择app id,提醒一下,选择app id要和你工程的Bundle Identifier对应,新手应该找的到Bundle Identifier吧,找不到看下面的图,我截了张图
p10.png

选择完app id ,一直点击Continue,进入如图界面
p5.png

让选择文件,还记得我们刚开始用钥匙串生成的.certSigningRequest在哪放着吧,就选择它就对了,选择完之后,接着往下面走,把证书下载下来之后,双击就放到钥匙串里面了,这时候推送证书就生成完了
第三步:生成p12文件
我们最终放到环信后台的证书是p12证书,打开钥匙串,找到我们刚才生成的证书,右键找到导出选项,如图
p6.png

点击导出“Apple Development.....”之后进入下个界面,如图:
p7.png

红色圈着的部分,储存为:里面要写上证书的名字,名字随便起,不过要记住这个名字,一会在环信后台上传证书还要用到这个名字,名字填完之后,点击储存,储存的位置要记住,一会还要用这个文件,这个文件就是我们要上传到环信的后台的p12文件,(储存的时候会让你输入密码,这个密码你要记住,一会上传到环信后台证书的时候要用到这个密码),这时候p12文件就制作完成了。
第四步:把p12文件上传到环信后台
打开我们的环信后台,找到 推送证书-ios,下面有上传证书的地方,如图
p8.png

证书名称:就是我们刚才生成p12的名称,我的叫zhuma_test
证书:就是我们刚才生成的p12证书,找到传上去
证书密码:生成p12证书的时候输入的密码(不是瞎输的吧,能记得吗,记不得重新导p12文件吧)
证书类型:我生成的是开发环境的证书,就勾选开发环境
填完之后就可以上传了,上传成功,就ok了
0
评论

NIUDAY 11.23 北京站 |环信CEO刘俊彦现场讲述人工智能赋能客户互动之现状与未来 刘俊彦 人工智能 环信 七牛云

beyond 发表了文章 • 50 次浏览 • 2018-11-15 16:36 • 来自相关话题

2018 年是见证「奇迹」的一年。AI 从多年的热门话题中开始走下神坛,逐渐深入到了各个行业,加速经济结构优化及行业智慧化升级,AI 已不再是难以企及的神话而是可触摸的美好未来。 政策支持加上资本推动,无论是从新兴行业还是传统行业都出现了人工智能方面的布局者和佼佼者,智慧教育、移动社交、智能语音、智慧客服、传统媒体等行业都在突破技术上和流程上的难点和困惑。

11 月 23 日,一场由七牛云主办主题为「AI 产业技术的渗透与融合」的 NIUDAY 小牛汇共享日将在北京举行。会上,将邀请众多行业内知名企业及技术专家,针对当前 AI 在技术上以及行业中遇到的一些壁垒问题,进行深入探讨和分享。

智慧教育、移动未来,体验指尖上的 AI

在此次活动中,除行业专家的视点剖析之外,对于 AI 与行业的渗透与融合度是理论大于实践亦或是理论与实践已完美结合,已经应用的行业代表案例有哪些?对于这些问题,大家可以在此次活动中得以解惑。




刘俊彦
手机电商、手机软件系统的快速更迭让在线客服、移动客服有了更大的发展空间,成了市场中的一片蓝海,究竟现状如何、客户体验的真实反馈是什么?环信 CEO 刘俊彦先生带来的《人工智能赋能客户互动之现状与未来》让你更贴近生活,更能体会 AI 的无处不在。




谢华亮

科技发展教育先行,技术与教育的结合又将碰撞出怎样的火花,会上,将有好未来 SEG 智慧教育事业部技术总监谢华亮带来主题为《AI 在教育行业中的应用》的精彩演讲。




佘超杰

移动互联网的发展已影响到我们身边的每一个人,游戏、娱乐、社交等手机平台的火爆更是带来了巨大的信息流量。但如何应对这些突然爆发的信息流,如何让产品与技术更好地去结合以吸引更多的受众人群,成为人们关注的话题。以作为专注于移动互联网社交的知名企业 Blued 为例,实践与技术并重的技术专家佘超杰将分享给我们《AI 在 Blued 上的应用》。
                
连线行业专家,洞悉专业视点

人工智能技术在飞速发展过程中得到了国家以及政府的极大关注与大力支持,中国人工智能产业发展联盟作为国家发展改革委、科学技术部、工业和信息化部、中央网信办四部委共同指导下的人工智能产业权威联盟机构,也将加入到此次 NIUDAY 小牛汇共享日活动中来。





孙明俊

中国信息通信研究院人工智能部副主任,中国人工智能产业发展联盟总体组组长、数据中心联盟秘书长孙明俊女士将出席本次活动,并将从行业专家的角度带来主题为《人工智能产业发展水平分析》的演讲,向大家解析产业发展水平、分享行业利好政策等。

战略签约中国网 传统与创新的再突破





杨新华

值得一提的是,本次 NIUDAY 小牛汇共享日活动上七牛云将与中国互联网新闻中心举行战略合作签约仪式。届时,中国互联网新闻中心·中国网副总编辑杨新华先生将带来《AI 如何讲好中国故事》的演讲,介绍七牛云在未来媒体平台构建中发挥的作用,以及正处于变革中的媒体行业对 AI 的思考。

当然作为本次 NIUDAY 小牛汇活动的主办方,七牛云也准备了满满的干货带给大家。七牛云技术总监陈超《数据智能时代的智慧工厂实践》、七牛云人工智能实验室资深产品经理杨叶青《一站式审核助力无忧运营》带你体会七牛云两大重要产品线的技术与发展。

更多大咖嘉宾,请往下看~ 





关于环信机器人:

环信机器人已经在中通快递、新东方、天津农商行、宜家、环球捕手等头部企业的“双十一”中发挥着中流砥柱的作用,机器人可以帮助解决80%以上的常见问题,在售后环节能替代超90%的人工,轻松实现7*24无间断客户服务,AI从此让我们的客服人员告别苦逼,云淡风轻,笑对各种节假日促销、狂欢节。

目前,环信机器人已经广泛服务于包括保险、证券、银行、教育、物流、商旅、电商、汽车等行业的数万客户,日机器人会话超千万条。
活动报名地址:http://www.huodongxing.com/event/2464773427600 查看全部

2018 年是见证「奇迹」的一年。AI 从多年的热门话题中开始走下神坛,逐渐深入到了各个行业,加速经济结构优化及行业智慧化升级,AI 已不再是难以企及的神话而是可触摸的美好未来。 政策支持加上资本推动,无论是从新兴行业还是传统行业都出现了人工智能方面的布局者和佼佼者,智慧教育、移动社交、智能语音、智慧客服、传统媒体等行业都在突破技术上和流程上的难点和困惑。



11 月 23 日,一场由七牛云主办主题为「AI 产业技术的渗透与融合」的 NIUDAY 小牛汇共享日将在北京举行。会上,将邀请众多行业内知名企业及技术专家,针对当前 AI 在技术上以及行业中遇到的一些壁垒问题,进行深入探讨和分享。

智慧教育、移动未来,体验指尖上的 AI

在此次活动中,除行业专家的视点剖析之外,对于 AI 与行业的渗透与融合度是理论大于实践亦或是理论与实践已完美结合,已经应用的行业代表案例有哪些?对于这些问题,大家可以在此次活动中得以解惑。

微信图片_20181115162453.jpg

刘俊彦


手机电商、手机软件系统的快速更迭让在线客服、移动客服有了更大的发展空间,成了市场中的一片蓝海,究竟现状如何、客户体验的真实反馈是什么?环信 CEO 刘俊彦先生带来的《人工智能赋能客户互动之现状与未来》让你更贴近生活,更能体会 AI 的无处不在。

微信图片_20181115162531.jpg

谢华亮



科技发展教育先行,技术与教育的结合又将碰撞出怎样的火花,会上,将有好未来 SEG 智慧教育事业部技术总监谢华亮带来主题为《AI 在教育行业中的应用》的精彩演讲。

微信图片_20181115162544.jpg

佘超杰



移动互联网的发展已影响到我们身边的每一个人,游戏、娱乐、社交等手机平台的火爆更是带来了巨大的信息流量。但如何应对这些突然爆发的信息流,如何让产品与技术更好地去结合以吸引更多的受众人群,成为人们关注的话题。以作为专注于移动互联网社交的知名企业 Blued 为例,实践与技术并重的技术专家佘超杰将分享给我们《AI 在 Blued 上的应用》。
                
连线行业专家,洞悉专业视点

人工智能技术在飞速发展过程中得到了国家以及政府的极大关注与大力支持,中国人工智能产业发展联盟作为国家发展改革委、科学技术部、工业和信息化部、中央网信办四部委共同指导下的人工智能产业权威联盟机构,也将加入到此次 NIUDAY 小牛汇共享日活动中来。

微信图片_20181115162600.jpg


孙明俊



中国信息通信研究院人工智能部副主任,中国人工智能产业发展联盟总体组组长、数据中心联盟秘书长孙明俊女士将出席本次活动,并将从行业专家的角度带来主题为《人工智能产业发展水平分析》的演讲,向大家解析产业发展水平、分享行业利好政策等。

战略签约中国网 传统与创新的再突破

微信图片_20181115162615.jpg


杨新华



值得一提的是,本次 NIUDAY 小牛汇共享日活动上七牛云将与中国互联网新闻中心举行战略合作签约仪式。届时,中国互联网新闻中心·中国网副总编辑杨新华先生将带来《AI 如何讲好中国故事》的演讲,介绍七牛云在未来媒体平台构建中发挥的作用,以及正处于变革中的媒体行业对 AI 的思考。

当然作为本次 NIUDAY 小牛汇活动的主办方,七牛云也准备了满满的干货带给大家。七牛云技术总监陈超《数据智能时代的智慧工厂实践》、七牛云人工智能实验室资深产品经理杨叶青《一站式审核助力无忧运营》带你体会七牛云两大重要产品线的技术与发展。

更多大咖嘉宾,请往下看~ 

5bed2dcd27fe6.jpg

关于环信机器人:

环信机器人已经在中通快递、新东方、天津农商行、宜家、环球捕手等头部企业的“双十一”中发挥着中流砥柱的作用,机器人可以帮助解决80%以上的常见问题,在售后环节能替代超90%的人工,轻松实现7*24无间断客户服务,AI从此让我们的客服人员告别苦逼,云淡风轻,笑对各种节假日促销、狂欢节。

目前,环信机器人已经广泛服务于包括保险、证券、银行、教育、物流、商旅、电商、汽车等行业的数万客户,日机器人会话超千万条。
活动报名地址:http://www.huodongxing.com/event/2464773427600
10
回复

收集基于环信SDK开发的开源项目 开源项目

回复

Tayler 回复了问题 • 13 人关注 • 11306 次浏览 • 2018-10-30 17:59 • 来自相关话题

4
评论

【开源项目】全国首个开源直播小程序源码 环信公开课 小程序 直播

beyond 发表了文章 • 3066 次浏览 • 2018-07-20 17:30 • 来自相关话题

今天你看直播了吗?拥有10亿微信生态用户的小程序已经成为了继移动互联后的又一个现象级风口,随着微信小程序对外开放实时音视频录制及播放等更多连接能力,小程序与直播强强联合,在各行各业找到了非常多的玩法,小程序直播相比微信直播和APP直播更加简洁、流畅、低延时、多入口等众多优势迅速向商业直播领域及泛娱乐直播领域蔓延。从小游戏、内容付费、工具、大数据、社交电商创业者到传统品牌商们,都在努力搭上小程序直播这辆快车,以免错过微信生态里新的流量洼地。
 





作为一名环信生态圈资深开发者,本着对技术的热衷,对环信的眷恋和对党的忠诚,基于环信即时通讯云写了“直播购物小程序”,目前项目源码已全部免费开放,希望对有需求的企业和开发者提供一个思路和参考。
直播购物小程序源码github地址:https://github.com/YuTongNetworkTechnology/wechat_live/tree/master 
git打不开可直接点下面链接下载


小程序直播demo_2018-06-21.zip







直播购物小程序运行预览图 
 
小程序体验指南(仅需两步):
 
1、下载微信小程序开发工具,下载地址:https://developers.weixin.qq.c ... .html 
 




2、导入源码:将附件的源码解压直接导入 







环信小程序直播技术文档
一、 使用的技术
1、 环信IM直播室。
2、 微信小程序实时音视频播放组件live-player。
3、 推流软件(obs、易推流)等推流。
4、 视频流服务器(UCLOUD、七牛、腾讯)等视频流服务器。
二、 系统使用流程。
1、 视频推流软件将视频流推到流服务器。
2、 打开视频直播demo小程序注册环信账号。
3、 进入软件直播室进行测试。
三、 技术流程及使用的SDk
1、 注册环信账号
打开https://www.easemob.com/ 环信官网,点击右上角注册按钮,选择[注册即时通讯云]




填写对相关信息进行注册





注册成功后进行登录




注:新注册用户需进行账号的认证。
2、 直播应用创建
登录成功点击应用列表选择创建应用




输入应用名称等信息
 





创建成功后点击应用进入





需要注意的是应用的OrgName 和AppName这两个是以后都需要用到的两个参数变量




3、 直播创建
1)在创建直播之前需要对应用进行设置首先需要设置应用的直播流地址
第一步获取应用管理员的Tokencurl -X POST "https://a1.easemob.com/[应用OrgName]/[应用AppName]/token" -d '{"grant_type":"client_credentials","client_id":"[应用client_id]","client_secret":"[应用] client_secret"}'返回格式{
"access_token":"YWMtWY779DgJEeS2h9OR7fw4QgAAAUmO4Qukwd9cfJSpkWHiOa7MCSk0MrkVIco",
"expires_in":5184000,
"application":"c03b3e30-046a-11e4-8ed1-5701cdaaa0e4"












第二步设置直播流地址curl -X POST -H "Authorization: Bearer [管理员Token]" " https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms/stream_url -d '{"pc_pull":"[pc拉流地址]","pc_push":"[pc推流地址]","mobile_pull":"[手机拉流地址]","mobile_push":"[手机推流地址]"}'"成功返回格式:{
"action": "post",
"application": "e1a09de0-0e03-11e7-ad8e-a1d913615409",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"pc_pull": true,
"mobile_push": true,
"mobile_pull": true,
"pc_push": true
},
"timestamp": 1494084474885,
"duration": 1,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












2)创建主播
点击IM用户





点击注册IM用户





填写用户信息





创建用户的过程同样也可以通过REST API形式进行curl -X POST -i " https://a1.easemob.com/[应用OrgName]/[应用AppName]/users" -d '{"username":"[用户名]","password":"[密码]"}'
注:应用必须为开放注册





将注册的用户添加为主播curl -X POST -H "Authorization: [管理员Token]" https://a1.easemob.com/[应用OrgName]/[应用AppName]/super_admin -d'{"superadmin":"[IM用户名]"}'返回结果示例:{
"action": "post",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"result": "success"
},
"timestamp": 1496236798886,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












3)创建直播
点击直播





点击新建房间





填写房间信息




创建房间同时也可以使用REST API形式进行详情可以查看http://docs.easemob.com/im/live/server-integration环信官方文档。
4、 小程序demo集成使用
小程序直播购物demo集成官方WebIM SDK详情请查看https://github.com/easemob/webim-weixin-xcx
Demo具体配置如下
打开demo 下sdk配置文件





修改appkey为自己应用的appkey





打开pages/live/index.js修改房间默认拉流地址及直播间房间号





四、 扩展说明
Demo中房间为固定测试房间,实际使用中应获取环信直播的房间信息及房间列表。具体如下:
获取直播间列表:curl -X GET -H "Authorization: Bearer [用户Token]" https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms?ongoing=true&limit=[获取数量]&cursor=[游标地址(不填写为充开始查询)]
响应:{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"params": {
"cursor": [
"ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6MzE"
],
"ongoing": [
"true"
],
"limit": [
"2"
]
},
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": [
{
"id": "1924",
"chatroom_id": "17177265635330",
"title": "具体了",
"desc": "就咯",
"startTime": 1495779917352,
"endTime": 1495779917352,
"anchor": "wuls",
"gift_count": 0,
"praise_count": 0,
"current_user_count": 8,
"max_user_count": 9,
"status": "ongoing",
"cover_picture_url": "",
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1"
},
{
"id": "1922",
"chatroom_id": "17175003856897",
"title": "香山",
"desc": "随便",
"startTime": 1495777760957,
"endTime": 1495777760957,
"anchor": "sx001",
"gift_count": 0,
"praise_count": 8,
"current_user_count": 1,
"max_user_count": 3,
"status": "ongoing",
"cover_picture_url": "http://127.0.0.1:8080/easemob- ... ot%3B,
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1"
}
],
"timestamp": 1496303336669,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"cursor": "ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6NDk",
"count": 2
}












获取直播间详情:curl -X GET -H "Authorization: Bearer [用户Token]" " https://a1.easemob.com/[应用OrgName]/[应用AppName]/[房间id]/status"响应:{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"liveRoomID": "1946",
"status": "ongoing"
},
"timestamp": 1496234759930,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"count": 0
}














 
使用环信直播购物小程序遇到任何问题欢迎跟帖讨论。 查看全部
今天你看直播了吗?
拥有10亿微信生态用户的小程序已经成为了继移动互联后的又一个现象级风口,随着微信小程序对外开放实时音视频录制及播放等更多连接能力,小程序与直播强强联合,在各行各业找到了非常多的玩法,小程序直播相比微信直播和APP直播更加简洁、流畅、低延时、多入口等众多优势迅速向商业直播领域及泛娱乐直播领域蔓延。从小游戏、内容付费、工具、大数据、社交电商创业者到传统品牌商们,都在努力搭上小程序直播这辆快车,以免错过微信生态里新的流量洼地。
 
微信图片_20180725162426.jpg


作为一名环信生态圈资深开发者,本着对技术的热衷,对环信的眷恋和对党的忠诚,基于环信即时通讯云写了“直播购物小程序”,目前项目源码已全部免费开放,希望对有需求的企业和开发者提供一个思路和参考。
直播购物小程序源码github地址:https://github.com/YuTongNetworkTechnology/wechat_live/tree/master 
git打不开可直接点下面链接下载



预览图.jpg

直播购物小程序运行预览图 
 
小程序体验指南(仅需两步):
 
1、下载微信小程序开发工具,下载地址:https://developers.weixin.qq.c ... .html 
 
Catch9A07(07-20-17-38-30).jpg

2、导入源码:将附件的源码解压直接导入 


Catch1C69(07-20-17-38-30).jpg


环信小程序直播技术文档
一、 使用的技术
1、 环信IM直播室。
2、 微信小程序实时音视频播放组件live-player。
3、 推流软件(obs、易推流)等推流。
4、 视频流服务器(UCLOUD、七牛、腾讯)等视频流服务器。
二、 系统使用流程。
1、 视频推流软件将视频流推到流服务器。
2、 打开视频直播demo小程序注册环信账号。
3、 进入软件直播室进行测试。
三、 技术流程及使用的SDk
1、 注册环信账号
打开https://www.easemob.com/ 环信官网,点击右上角注册按钮,选择[注册即时通讯云]
1.png

填写对相关信息进行注册

2.png

注册成功后进行登录
3.png

注:新注册用户需进行账号的认证。
2、 直播应用创建
登录成功点击应用列表选择创建应用
4.png

输入应用名称等信息
 

5.png

创建成功后点击应用进入

6.png

需要注意的是应用的OrgName 和AppName这两个是以后都需要用到的两个参数变量
7.png

3、 直播创建
1)在创建直播之前需要对应用进行设置首先需要设置应用的直播流地址
第一步获取应用管理员的Token
curl -X POST "https://a1.easemob.com/[应用OrgName]/[应用AppName]/token" -d '{"grant_type":"client_credentials","client_id":"[应用client_id]","client_secret":"[应用] client_secret"}'
返回格式
{
"access_token":"YWMtWY779DgJEeS2h9OR7fw4QgAAAUmO4Qukwd9cfJSpkWHiOa7MCSk0MrkVIco",
"expires_in":5184000,
"application":"c03b3e30-046a-11e4-8ed1-5701cdaaa0e4"












第二步设置直播流地址
curl -X POST -H "Authorization: Bearer [管理员Token]"  " https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms/stream_url -d '{"pc_pull":"[pc拉流地址]","pc_push":"[pc推流地址]","mobile_pull":"[手机拉流地址]","mobile_push":"[手机推流地址]"}'"
成功返回格式:
{
"action": "post",
"application": "e1a09de0-0e03-11e7-ad8e-a1d913615409",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"pc_pull": true,
"mobile_push": true,
"mobile_pull": true,
"pc_push": true
},
"timestamp": 1494084474885,
"duration": 1,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












2)创建主播
点击IM用户

8.png

点击注册IM用户

9.png

填写用户信息

10.png

创建用户的过程同样也可以通过REST API形式进行
curl -X POST -i " https://a1.easemob.com/[应用OrgName]/[应用AppName]/users" -d '{"username":"[用户名]","password":"[密码]"}'

注:应用必须为开放注册

11.png

将注册的用户添加为主播
curl -X POST -H "Authorization: [管理员Token]"  https://a1.easemob.com/[应用OrgName]/[应用AppName]/super_admin -d'{"superadmin":"[IM用户名]"}'
返回结果示例:
{
"action": "post",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"result": "success"
},
"timestamp": 1496236798886,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












3)创建直播
点击直播

12.png

点击新建房间

13.png

填写房间信息
14.png

创建房间同时也可以使用REST API形式进行详情可以查看http://docs.easemob.com/im/live/server-integration环信官方文档。
4、 小程序demo集成使用
小程序直播购物demo集成官方WebIM SDK详情请查看https://github.com/easemob/webim-weixin-xcx
Demo具体配置如下
打开demo 下sdk配置文件

15.png

修改appkey为自己应用的appkey

16.png

打开pages/live/index.js修改房间默认拉流地址及直播间房间号

17.png

四、 扩展说明
Demo中房间为固定测试房间,实际使用中应获取环信直播的房间信息及房间列表。具体如下:
获取直播间列表:
curl -X GET -H "Authorization: Bearer  [用户Token]"  https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms?ongoing=true&limit=[获取数量]&cursor=[游标地址(不填写为充开始查询)]

响应:
{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"params": {
"cursor": [
"ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6MzE"
],
"ongoing": [
"true"
],
"limit": [
"2"
]
},
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": [
{
"id": "1924",
"chatroom_id": "17177265635330",
"title": "具体了",
"desc": "就咯",
"startTime": 1495779917352,
"endTime": 1495779917352,
"anchor": "wuls",
"gift_count": 0,
"praise_count": 0,
"current_user_count": 8,
"max_user_count": 9,
"status": "ongoing",
"cover_picture_url": "",
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1"
},
{
"id": "1922",
"chatroom_id": "17175003856897",
"title": "香山",
"desc": "随便",
"startTime": 1495777760957,
"endTime": 1495777760957,
"anchor": "sx001",
"gift_count": 0,
"praise_count": 8,
"current_user_count": 1,
"max_user_count": 3,
"status": "ongoing",
"cover_picture_url": "http://127.0.0.1:8080/easemob- ... ot%3B,
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1"
}
],
"timestamp": 1496303336669,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"cursor": "ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6NDk",
"count": 2
}












获取直播间详情:
curl -X GET -H "Authorization: Bearer [用户Token]" " https://a1.easemob.com/[应用OrgName]/[应用AppName]/[房间id]/status"
响应:
{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"liveRoomID": "1946",
"status": "ongoing"
},
"timestamp": 1496234759930,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"count": 0
}














 
使用环信直播购物小程序遇到任何问题欢迎跟帖讨论。
19
评论

【新手快速入门】集成环信常见问题+解决方案汇总 常见问题

dujiepeng 发表了文章 • 17966 次浏览 • 2017-05-22 15:51 • 来自相关话题

   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇
APNs证书创建和上传到环信后台头像昵称的简述和处理方案音视频离线推送Demo实现环信服务器聊天记录保存多久?离线收不到好友请求IOS中环信聊天窗口如何实现文件发送和预览的功能ios集成常见问题环信推送的一些常见问题实现名片|红包|话题聊天室等自定义cell
 
Android篇
Android sdk 的两种导入方式环信3.0SDK集成小米推送教程EaseUI库中V4、v7包冲突解决方案Android EaseUI里的百度地图替换为高德地图android扩展消息(名片集成)关于会话列表的置顶聊天java.lang.UnsatisfiedLinkError: 的问题android 端 app 后台被杀死收不到消息的解决方案
昵称头像篇
android中如何显示开发者服务器上的昵称和头像 Android中显示头像(接上一篇文章看)环信(Android)设置头像和昵称的方法(最简单暴力的基于环信demo的集成)IOS中如何显示开发者服务器上的昵称和头像【环信公开课第12期视频回放】-所有关于环信IM昵称头像的问题听这课就够了
 
直播篇
一言不合你就搞个直播APP
 
客服集成
IM-SDK和客服SDK并存开发指南—Android篇IM-SDK和客服SDK并存开发指南—iOS篇
 
开源项目
Android简版demoios简版demo凡信2.0:超仿微信的开源项目 凡信3.0:携直播和红包而来高仿微信:Github 3,515 Star方圆十里:环信编程大赛冠军项目泛聊:定一个小目标写一个QQSlack聊天机器人:一天时间做一个聊天机器人TV视频通话:在电视上视频通话视频通话:Android手机视频通话酷信:ios高仿微信公众号助手:与订阅用户聊天沟通
 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
  查看全部
   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇

 
Android篇

昵称头像篇

 
直播篇
  1. 一言不合你就搞个直播APP

 
客服集成
  1. IM-SDK和客服SDK并存开发指南—Android篇
  2. IM-SDK和客服SDK并存开发指南—iOS篇

 
开源项目

 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
 
1
回复

语音视频聊天 挂断如何有相应的响应(比如 对方已取消 iOS 端) 挂断处理 语音 视频 iOS

回复

ζ 回复了问题 • 1 人关注 • 372 次浏览 • 2018-10-23 14:27 • 来自相关话题

0
回复

视频通话的时候能否对其中的语音进行录制? 语音录制 视频通话 iOS

回复

小小聪明屋 发起了问题 • 1 人关注 • 350 次浏览 • 2018-10-18 16:32 • 来自相关话题

1
回复

OC_SDK集成参考视频 建议 环信 iOS

回复

beyond 回复了问题 • 2 人关注 • 352 次浏览 • 2018-07-26 10:48 • 来自相关话题

4
回复

iOS获取语音信息 环信_iOS iOS

回复

beyond 回复了问题 • 3 人关注 • 445 次浏览 • 2018-07-20 18:52 • 来自相关话题

2
回复
0
回复

getAllIgnoredGroupIdsh获取不到屏蔽推送的群组 iOS

回复

RinoWu 发起了问题 • 1 人关注 • 405 次浏览 • 2018-06-14 16:06 • 来自相关话题

1
回复
1
回复

老哥修改EaseUI中#import <SDWebImage/UIImageView+WebCache.h>会有一下影响吧 iOS

回复

KevinGong 回复了问题 • 2 人关注 • 605 次浏览 • 2018-06-06 15:01 • 来自相关话题

1
回复

都添加哪些库,视频里看不清啊? 环信 iOS

回复

KevinGong 回复了问题 • 2 人关注 • 408 次浏览 • 2018-06-05 11:52 • 来自相关话题

2
回复

user not login iOS

回复

差一点是帅哥 回复了问题 • 3 人关注 • 1183 次浏览 • 2018-05-09 20:56 • 来自相关话题

1
回复

Could not successfully update network info during initialization这是什么原因啊? 环信 iOS

回复

驱马历长洲 回复了问题 • 2 人关注 • 4938 次浏览 • 2018-04-29 15:05 • 来自相关话题

1
回复

集成EaseUI发送消息成功后怎么获得发送的消息内容 iOS EaseUI

回复

磨不平的棱角。 回复了问题 • 1 人关注 • 551 次浏览 • 2018-04-09 15:14 • 来自相关话题

0
回复

集成EaseUI怎么让显示昵称而不是用户名 iOS

回复

磨不平的棱角。 发起了问题 • 1 人关注 • 657 次浏览 • 2018-03-31 09:13 • 来自相关话题

0
回复

iOS 提示“缩略图获取失败” 缩略图 iOS

回复

磨不平的棱角。 发起了问题 • 1 人关注 • 520 次浏览 • 2018-03-29 10:04 • 来自相关话题

0
回复

环信更新证书问题 iOS 证书

回复

微~凉 发起了问题 • 1 人关注 • 586 次浏览 • 2018-03-27 16:53 • 来自相关话题

0
回复

环信创建群 iOS iOS 环信 iOS 好友 环信 ios 环信_iOS

回复

未完成 发起了问题 • 1 人关注 • 732 次浏览 • 2018-02-13 16:13 • 来自相关话题

0
回复

643dc0e4d22c2079f10522168b4ed470求助 iOS

回复

雨夜黑瞳 发起了问题 • 1 人关注 • 626 次浏览 • 2018-02-09 12:57 • 来自相关话题

0
回复

i环信发送文件 消息, 自动存储嘛? iOS 环信_iOS

回复

Joshua1 发起了问题 • 1 人关注 • 2848 次浏览 • 2018-01-30 19:51 • 来自相关话题

0
回复

iOS登录报错203? iOS 登录报错 登录203

回复

shallwe 发起了问题 • 1 人关注 • 660 次浏览 • 2018-01-30 17:24 • 来自相关话题

1
回复

iOS端,发送消息时调用的方法内部有关于插入信息到数据库吗? iOS

回复

chickenho 回复了问题 • 1 人关注 • 749 次浏览 • 2018-01-02 20:12 • 来自相关话题

0
回复

ios 2.0集成 ios 导入sdk问题 iOS ios 2.3.1环信sdk

回复

猛追湾东街 发起了问题 • 1 人关注 • 746 次浏览 • 2017-12-16 22:41 • 来自相关话题

0
评论

NIUDAY 11.23 北京站 |环信CEO刘俊彦现场讲述人工智能赋能客户互动之现状与未来 刘俊彦 人工智能 环信 七牛云

beyond 发表了文章 • 50 次浏览 • 2018-11-15 16:36 • 来自相关话题

2018 年是见证「奇迹」的一年。AI 从多年的热门话题中开始走下神坛,逐渐深入到了各个行业,加速经济结构优化及行业智慧化升级,AI 已不再是难以企及的神话而是可触摸的美好未来。 政策支持加上资本推动,无论是从新兴行业还是传统行业都出现了人工智能方面的布局者和佼佼者,智慧教育、移动社交、智能语音、智慧客服、传统媒体等行业都在突破技术上和流程上的难点和困惑。

11 月 23 日,一场由七牛云主办主题为「AI 产业技术的渗透与融合」的 NIUDAY 小牛汇共享日将在北京举行。会上,将邀请众多行业内知名企业及技术专家,针对当前 AI 在技术上以及行业中遇到的一些壁垒问题,进行深入探讨和分享。

智慧教育、移动未来,体验指尖上的 AI

在此次活动中,除行业专家的视点剖析之外,对于 AI 与行业的渗透与融合度是理论大于实践亦或是理论与实践已完美结合,已经应用的行业代表案例有哪些?对于这些问题,大家可以在此次活动中得以解惑。




刘俊彦
手机电商、手机软件系统的快速更迭让在线客服、移动客服有了更大的发展空间,成了市场中的一片蓝海,究竟现状如何、客户体验的真实反馈是什么?环信 CEO 刘俊彦先生带来的《人工智能赋能客户互动之现状与未来》让你更贴近生活,更能体会 AI 的无处不在。




谢华亮

科技发展教育先行,技术与教育的结合又将碰撞出怎样的火花,会上,将有好未来 SEG 智慧教育事业部技术总监谢华亮带来主题为《AI 在教育行业中的应用》的精彩演讲。




佘超杰

移动互联网的发展已影响到我们身边的每一个人,游戏、娱乐、社交等手机平台的火爆更是带来了巨大的信息流量。但如何应对这些突然爆发的信息流,如何让产品与技术更好地去结合以吸引更多的受众人群,成为人们关注的话题。以作为专注于移动互联网社交的知名企业 Blued 为例,实践与技术并重的技术专家佘超杰将分享给我们《AI 在 Blued 上的应用》。
                
连线行业专家,洞悉专业视点

人工智能技术在飞速发展过程中得到了国家以及政府的极大关注与大力支持,中国人工智能产业发展联盟作为国家发展改革委、科学技术部、工业和信息化部、中央网信办四部委共同指导下的人工智能产业权威联盟机构,也将加入到此次 NIUDAY 小牛汇共享日活动中来。





孙明俊

中国信息通信研究院人工智能部副主任,中国人工智能产业发展联盟总体组组长、数据中心联盟秘书长孙明俊女士将出席本次活动,并将从行业专家的角度带来主题为《人工智能产业发展水平分析》的演讲,向大家解析产业发展水平、分享行业利好政策等。

战略签约中国网 传统与创新的再突破





杨新华

值得一提的是,本次 NIUDAY 小牛汇共享日活动上七牛云将与中国互联网新闻中心举行战略合作签约仪式。届时,中国互联网新闻中心·中国网副总编辑杨新华先生将带来《AI 如何讲好中国故事》的演讲,介绍七牛云在未来媒体平台构建中发挥的作用,以及正处于变革中的媒体行业对 AI 的思考。

当然作为本次 NIUDAY 小牛汇活动的主办方,七牛云也准备了满满的干货带给大家。七牛云技术总监陈超《数据智能时代的智慧工厂实践》、七牛云人工智能实验室资深产品经理杨叶青《一站式审核助力无忧运营》带你体会七牛云两大重要产品线的技术与发展。

更多大咖嘉宾,请往下看~ 





关于环信机器人:

环信机器人已经在中通快递、新东方、天津农商行、宜家、环球捕手等头部企业的“双十一”中发挥着中流砥柱的作用,机器人可以帮助解决80%以上的常见问题,在售后环节能替代超90%的人工,轻松实现7*24无间断客户服务,AI从此让我们的客服人员告别苦逼,云淡风轻,笑对各种节假日促销、狂欢节。

目前,环信机器人已经广泛服务于包括保险、证券、银行、教育、物流、商旅、电商、汽车等行业的数万客户,日机器人会话超千万条。
活动报名地址:http://www.huodongxing.com/event/2464773427600 查看全部

2018 年是见证「奇迹」的一年。AI 从多年的热门话题中开始走下神坛,逐渐深入到了各个行业,加速经济结构优化及行业智慧化升级,AI 已不再是难以企及的神话而是可触摸的美好未来。 政策支持加上资本推动,无论是从新兴行业还是传统行业都出现了人工智能方面的布局者和佼佼者,智慧教育、移动社交、智能语音、智慧客服、传统媒体等行业都在突破技术上和流程上的难点和困惑。



11 月 23 日,一场由七牛云主办主题为「AI 产业技术的渗透与融合」的 NIUDAY 小牛汇共享日将在北京举行。会上,将邀请众多行业内知名企业及技术专家,针对当前 AI 在技术上以及行业中遇到的一些壁垒问题,进行深入探讨和分享。

智慧教育、移动未来,体验指尖上的 AI

在此次活动中,除行业专家的视点剖析之外,对于 AI 与行业的渗透与融合度是理论大于实践亦或是理论与实践已完美结合,已经应用的行业代表案例有哪些?对于这些问题,大家可以在此次活动中得以解惑。

微信图片_20181115162453.jpg

刘俊彦


手机电商、手机软件系统的快速更迭让在线客服、移动客服有了更大的发展空间,成了市场中的一片蓝海,究竟现状如何、客户体验的真实反馈是什么?环信 CEO 刘俊彦先生带来的《人工智能赋能客户互动之现状与未来》让你更贴近生活,更能体会 AI 的无处不在。

微信图片_20181115162531.jpg

谢华亮



科技发展教育先行,技术与教育的结合又将碰撞出怎样的火花,会上,将有好未来 SEG 智慧教育事业部技术总监谢华亮带来主题为《AI 在教育行业中的应用》的精彩演讲。

微信图片_20181115162544.jpg

佘超杰



移动互联网的发展已影响到我们身边的每一个人,游戏、娱乐、社交等手机平台的火爆更是带来了巨大的信息流量。但如何应对这些突然爆发的信息流,如何让产品与技术更好地去结合以吸引更多的受众人群,成为人们关注的话题。以作为专注于移动互联网社交的知名企业 Blued 为例,实践与技术并重的技术专家佘超杰将分享给我们《AI 在 Blued 上的应用》。
                
连线行业专家,洞悉专业视点

人工智能技术在飞速发展过程中得到了国家以及政府的极大关注与大力支持,中国人工智能产业发展联盟作为国家发展改革委、科学技术部、工业和信息化部、中央网信办四部委共同指导下的人工智能产业权威联盟机构,也将加入到此次 NIUDAY 小牛汇共享日活动中来。

微信图片_20181115162600.jpg


孙明俊



中国信息通信研究院人工智能部副主任,中国人工智能产业发展联盟总体组组长、数据中心联盟秘书长孙明俊女士将出席本次活动,并将从行业专家的角度带来主题为《人工智能产业发展水平分析》的演讲,向大家解析产业发展水平、分享行业利好政策等。

战略签约中国网 传统与创新的再突破

微信图片_20181115162615.jpg


杨新华



值得一提的是,本次 NIUDAY 小牛汇共享日活动上七牛云将与中国互联网新闻中心举行战略合作签约仪式。届时,中国互联网新闻中心·中国网副总编辑杨新华先生将带来《AI 如何讲好中国故事》的演讲,介绍七牛云在未来媒体平台构建中发挥的作用,以及正处于变革中的媒体行业对 AI 的思考。

当然作为本次 NIUDAY 小牛汇活动的主办方,七牛云也准备了满满的干货带给大家。七牛云技术总监陈超《数据智能时代的智慧工厂实践》、七牛云人工智能实验室资深产品经理杨叶青《一站式审核助力无忧运营》带你体会七牛云两大重要产品线的技术与发展。

更多大咖嘉宾,请往下看~ 

5bed2dcd27fe6.jpg

关于环信机器人:

环信机器人已经在中通快递、新东方、天津农商行、宜家、环球捕手等头部企业的“双十一”中发挥着中流砥柱的作用,机器人可以帮助解决80%以上的常见问题,在售后环节能替代超90%的人工,轻松实现7*24无间断客户服务,AI从此让我们的客服人员告别苦逼,云淡风轻,笑对各种节假日促销、狂欢节。

目前,环信机器人已经广泛服务于包括保险、证券、银行、教育、物流、商旅、电商、汽车等行业的数万客户,日机器人会话超千万条。
活动报名地址:http://www.huodongxing.com/event/2464773427600
10
回复

收集基于环信SDK开发的开源项目 开源项目

回复

Tayler 回复了问题 • 13 人关注 • 11306 次浏览 • 2018-10-30 17:59 • 来自相关话题

4
评论

【开源项目】全国首个开源直播小程序源码 环信公开课 小程序 直播

beyond 发表了文章 • 3066 次浏览 • 2018-07-20 17:30 • 来自相关话题

今天你看直播了吗?拥有10亿微信生态用户的小程序已经成为了继移动互联后的又一个现象级风口,随着微信小程序对外开放实时音视频录制及播放等更多连接能力,小程序与直播强强联合,在各行各业找到了非常多的玩法,小程序直播相比微信直播和APP直播更加简洁、流畅、低延时、多入口等众多优势迅速向商业直播领域及泛娱乐直播领域蔓延。从小游戏、内容付费、工具、大数据、社交电商创业者到传统品牌商们,都在努力搭上小程序直播这辆快车,以免错过微信生态里新的流量洼地。
 





作为一名环信生态圈资深开发者,本着对技术的热衷,对环信的眷恋和对党的忠诚,基于环信即时通讯云写了“直播购物小程序”,目前项目源码已全部免费开放,希望对有需求的企业和开发者提供一个思路和参考。
直播购物小程序源码github地址:https://github.com/YuTongNetworkTechnology/wechat_live/tree/master 
git打不开可直接点下面链接下载


小程序直播demo_2018-06-21.zip







直播购物小程序运行预览图 
 
小程序体验指南(仅需两步):
 
1、下载微信小程序开发工具,下载地址:https://developers.weixin.qq.c ... .html 
 




2、导入源码:将附件的源码解压直接导入 







环信小程序直播技术文档
一、 使用的技术
1、 环信IM直播室。
2、 微信小程序实时音视频播放组件live-player。
3、 推流软件(obs、易推流)等推流。
4、 视频流服务器(UCLOUD、七牛、腾讯)等视频流服务器。
二、 系统使用流程。
1、 视频推流软件将视频流推到流服务器。
2、 打开视频直播demo小程序注册环信账号。
3、 进入软件直播室进行测试。
三、 技术流程及使用的SDk
1、 注册环信账号
打开https://www.easemob.com/ 环信官网,点击右上角注册按钮,选择[注册即时通讯云]




填写对相关信息进行注册





注册成功后进行登录




注:新注册用户需进行账号的认证。
2、 直播应用创建
登录成功点击应用列表选择创建应用




输入应用名称等信息
 





创建成功后点击应用进入





需要注意的是应用的OrgName 和AppName这两个是以后都需要用到的两个参数变量




3、 直播创建
1)在创建直播之前需要对应用进行设置首先需要设置应用的直播流地址
第一步获取应用管理员的Tokencurl -X POST "https://a1.easemob.com/[应用OrgName]/[应用AppName]/token" -d '{"grant_type":"client_credentials","client_id":"[应用client_id]","client_secret":"[应用] client_secret"}'返回格式{
"access_token":"YWMtWY779DgJEeS2h9OR7fw4QgAAAUmO4Qukwd9cfJSpkWHiOa7MCSk0MrkVIco",
"expires_in":5184000,
"application":"c03b3e30-046a-11e4-8ed1-5701cdaaa0e4"












第二步设置直播流地址curl -X POST -H "Authorization: Bearer [管理员Token]" " https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms/stream_url -d '{"pc_pull":"[pc拉流地址]","pc_push":"[pc推流地址]","mobile_pull":"[手机拉流地址]","mobile_push":"[手机推流地址]"}'"成功返回格式:{
"action": "post",
"application": "e1a09de0-0e03-11e7-ad8e-a1d913615409",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"pc_pull": true,
"mobile_push": true,
"mobile_pull": true,
"pc_push": true
},
"timestamp": 1494084474885,
"duration": 1,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












2)创建主播
点击IM用户





点击注册IM用户





填写用户信息





创建用户的过程同样也可以通过REST API形式进行curl -X POST -i " https://a1.easemob.com/[应用OrgName]/[应用AppName]/users" -d '{"username":"[用户名]","password":"[密码]"}'
注:应用必须为开放注册





将注册的用户添加为主播curl -X POST -H "Authorization: [管理员Token]" https://a1.easemob.com/[应用OrgName]/[应用AppName]/super_admin -d'{"superadmin":"[IM用户名]"}'返回结果示例:{
"action": "post",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"result": "success"
},
"timestamp": 1496236798886,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












3)创建直播
点击直播





点击新建房间





填写房间信息




创建房间同时也可以使用REST API形式进行详情可以查看http://docs.easemob.com/im/live/server-integration环信官方文档。
4、 小程序demo集成使用
小程序直播购物demo集成官方WebIM SDK详情请查看https://github.com/easemob/webim-weixin-xcx
Demo具体配置如下
打开demo 下sdk配置文件





修改appkey为自己应用的appkey





打开pages/live/index.js修改房间默认拉流地址及直播间房间号





四、 扩展说明
Demo中房间为固定测试房间,实际使用中应获取环信直播的房间信息及房间列表。具体如下:
获取直播间列表:curl -X GET -H "Authorization: Bearer [用户Token]" https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms?ongoing=true&limit=[获取数量]&cursor=[游标地址(不填写为充开始查询)]
响应:{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"params": {
"cursor": [
"ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6MzE"
],
"ongoing": [
"true"
],
"limit": [
"2"
]
},
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": [
{
"id": "1924",
"chatroom_id": "17177265635330",
"title": "具体了",
"desc": "就咯",
"startTime": 1495779917352,
"endTime": 1495779917352,
"anchor": "wuls",
"gift_count": 0,
"praise_count": 0,
"current_user_count": 8,
"max_user_count": 9,
"status": "ongoing",
"cover_picture_url": "",
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1"
},
{
"id": "1922",
"chatroom_id": "17175003856897",
"title": "香山",
"desc": "随便",
"startTime": 1495777760957,
"endTime": 1495777760957,
"anchor": "sx001",
"gift_count": 0,
"praise_count": 8,
"current_user_count": 1,
"max_user_count": 3,
"status": "ongoing",
"cover_picture_url": "http://127.0.0.1:8080/easemob- ... ot%3B,
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1"
}
],
"timestamp": 1496303336669,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"cursor": "ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6NDk",
"count": 2
}












获取直播间详情:curl -X GET -H "Authorization: Bearer [用户Token]" " https://a1.easemob.com/[应用OrgName]/[应用AppName]/[房间id]/status"响应:{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"liveRoomID": "1946",
"status": "ongoing"
},
"timestamp": 1496234759930,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"count": 0
}














 
使用环信直播购物小程序遇到任何问题欢迎跟帖讨论。 查看全部
今天你看直播了吗?
拥有10亿微信生态用户的小程序已经成为了继移动互联后的又一个现象级风口,随着微信小程序对外开放实时音视频录制及播放等更多连接能力,小程序与直播强强联合,在各行各业找到了非常多的玩法,小程序直播相比微信直播和APP直播更加简洁、流畅、低延时、多入口等众多优势迅速向商业直播领域及泛娱乐直播领域蔓延。从小游戏、内容付费、工具、大数据、社交电商创业者到传统品牌商们,都在努力搭上小程序直播这辆快车,以免错过微信生态里新的流量洼地。
 
微信图片_20180725162426.jpg


作为一名环信生态圈资深开发者,本着对技术的热衷,对环信的眷恋和对党的忠诚,基于环信即时通讯云写了“直播购物小程序”,目前项目源码已全部免费开放,希望对有需求的企业和开发者提供一个思路和参考。
直播购物小程序源码github地址:https://github.com/YuTongNetworkTechnology/wechat_live/tree/master 
git打不开可直接点下面链接下载



预览图.jpg

直播购物小程序运行预览图 
 
小程序体验指南(仅需两步):
 
1、下载微信小程序开发工具,下载地址:https://developers.weixin.qq.c ... .html 
 
Catch9A07(07-20-17-38-30).jpg

2、导入源码:将附件的源码解压直接导入 


Catch1C69(07-20-17-38-30).jpg


环信小程序直播技术文档
一、 使用的技术
1、 环信IM直播室。
2、 微信小程序实时音视频播放组件live-player。
3、 推流软件(obs、易推流)等推流。
4、 视频流服务器(UCLOUD、七牛、腾讯)等视频流服务器。
二、 系统使用流程。
1、 视频推流软件将视频流推到流服务器。
2、 打开视频直播demo小程序注册环信账号。
3、 进入软件直播室进行测试。
三、 技术流程及使用的SDk
1、 注册环信账号
打开https://www.easemob.com/ 环信官网,点击右上角注册按钮,选择[注册即时通讯云]
1.png

填写对相关信息进行注册

2.png

注册成功后进行登录
3.png

注:新注册用户需进行账号的认证。
2、 直播应用创建
登录成功点击应用列表选择创建应用
4.png

输入应用名称等信息
 

5.png

创建成功后点击应用进入

6.png

需要注意的是应用的OrgName 和AppName这两个是以后都需要用到的两个参数变量
7.png

3、 直播创建
1)在创建直播之前需要对应用进行设置首先需要设置应用的直播流地址
第一步获取应用管理员的Token
curl -X POST "https://a1.easemob.com/[应用OrgName]/[应用AppName]/token" -d '{"grant_type":"client_credentials","client_id":"[应用client_id]","client_secret":"[应用] client_secret"}'
返回格式
{
"access_token":"YWMtWY779DgJEeS2h9OR7fw4QgAAAUmO4Qukwd9cfJSpkWHiOa7MCSk0MrkVIco",
"expires_in":5184000,
"application":"c03b3e30-046a-11e4-8ed1-5701cdaaa0e4"












第二步设置直播流地址
curl -X POST -H "Authorization: Bearer [管理员Token]"  " https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms/stream_url -d '{"pc_pull":"[pc拉流地址]","pc_push":"[pc推流地址]","mobile_pull":"[手机拉流地址]","mobile_push":"[手机推流地址]"}'"
成功返回格式:
{
"action": "post",
"application": "e1a09de0-0e03-11e7-ad8e-a1d913615409",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"pc_pull": true,
"mobile_push": true,
"mobile_pull": true,
"pc_push": true
},
"timestamp": 1494084474885,
"duration": 1,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












2)创建主播
点击IM用户

8.png

点击注册IM用户

9.png

填写用户信息

10.png

创建用户的过程同样也可以通过REST API形式进行
curl -X POST -i " https://a1.easemob.com/[应用OrgName]/[应用AppName]/users" -d '{"username":"[用户名]","password":"[密码]"}'

注:应用必须为开放注册

11.png

将注册的用户添加为主播
curl -X POST -H "Authorization: [管理员Token]"  https://a1.easemob.com/[应用OrgName]/[应用AppName]/super_admin -d'{"superadmin":"[IM用户名]"}'
返回结果示例:
{
"action": "post",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"result": "success"
},
"timestamp": 1496236798886,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui"
}












3)创建直播
点击直播

12.png

点击新建房间

13.png

填写房间信息
14.png

创建房间同时也可以使用REST API形式进行详情可以查看http://docs.easemob.com/im/live/server-integration环信官方文档。
4、 小程序demo集成使用
小程序直播购物demo集成官方WebIM SDK详情请查看https://github.com/easemob/webim-weixin-xcx
Demo具体配置如下
打开demo 下sdk配置文件

15.png

修改appkey为自己应用的appkey

16.png

打开pages/live/index.js修改房间默认拉流地址及直播间房间号

17.png

四、 扩展说明
Demo中房间为固定测试房间,实际使用中应获取环信直播的房间信息及房间列表。具体如下:
获取直播间列表:
curl -X GET -H "Authorization: Bearer  [用户Token]"  https://a1.easemob.com/[应用OrgName]/[应用AppName]/liverooms?ongoing=true&limit=[获取数量]&cursor=[游标地址(不填写为充开始查询)]

响应:
{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"params": {
"cursor": [
"ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6MzE"
],
"ongoing": [
"true"
],
"limit": [
"2"
]
},
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": [
{
"id": "1924",
"chatroom_id": "17177265635330",
"title": "具体了",
"desc": "就咯",
"startTime": 1495779917352,
"endTime": 1495779917352,
"anchor": "wuls",
"gift_count": 0,
"praise_count": 0,
"current_user_count": 8,
"max_user_count": 9,
"status": "ongoing",
"cover_picture_url": "",
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1924_1"
},
{
"id": "1922",
"chatroom_id": "17175003856897",
"title": "香山",
"desc": "随便",
"startTime": 1495777760957,
"endTime": 1495777760957,
"anchor": "sx001",
"gift_count": 0,
"praise_count": 8,
"current_user_count": 1,
"max_user_count": 3,
"status": "ongoing",
"cover_picture_url": "http://127.0.0.1:8080/easemob- ... ot%3B,
"pc_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"pc_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_pull_url": "rtmp://vlive3.rtmp.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1",
"mobile_push_url": "rtmp://publish3.cdn.ucloud.com.cn/ucloud/easemob-demo_chatdemoui_1922_1"
}
],
"timestamp": 1496303336669,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"cursor": "ZGNiMjRmNGY1YjczYjlhYTNkYjk1MDY2YmEyNzFmODQ6aW06Y2hhdHJvb206ZWFzZW1vYi1kZW1vI2NoYXRkZW1vdWk6NDk",
"count": 2
}












获取直播间详情:
curl -X GET -H "Authorization: Bearer [用户Token]" " https://a1.easemob.com/[应用OrgName]/[应用AppName]/[房间id]/status"
响应:
{
"action": "get",
"application": "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
"uri": "http://127.0.0.1:8080/easemob- ... ot%3B,
"entities": [ ],
"data": {
"liveRoomID": "1946",
"status": "ongoing"
},
"timestamp": 1496234759930,
"duration": 0,
"organization": "easemob-demo",
"applicationName": "chatdemoui",
"count": 0
}














 
使用环信直播购物小程序遇到任何问题欢迎跟帖讨论。
19
评论

【新手快速入门】集成环信常见问题+解决方案汇总 常见问题

dujiepeng 发表了文章 • 17966 次浏览 • 2017-05-22 15:51 • 来自相关话题

   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇
APNs证书创建和上传到环信后台头像昵称的简述和处理方案音视频离线推送Demo实现环信服务器聊天记录保存多久?离线收不到好友请求IOS中环信聊天窗口如何实现文件发送和预览的功能ios集成常见问题环信推送的一些常见问题实现名片|红包|话题聊天室等自定义cell
 
Android篇
Android sdk 的两种导入方式环信3.0SDK集成小米推送教程EaseUI库中V4、v7包冲突解决方案Android EaseUI里的百度地图替换为高德地图android扩展消息(名片集成)关于会话列表的置顶聊天java.lang.UnsatisfiedLinkError: 的问题android 端 app 后台被杀死收不到消息的解决方案
昵称头像篇
android中如何显示开发者服务器上的昵称和头像 Android中显示头像(接上一篇文章看)环信(Android)设置头像和昵称的方法(最简单暴力的基于环信demo的集成)IOS中如何显示开发者服务器上的昵称和头像【环信公开课第12期视频回放】-所有关于环信IM昵称头像的问题听这课就够了
 
直播篇
一言不合你就搞个直播APP
 
客服集成
IM-SDK和客服SDK并存开发指南—Android篇IM-SDK和客服SDK并存开发指南—iOS篇
 
开源项目
Android简版demoios简版demo凡信2.0:超仿微信的开源项目 凡信3.0:携直播和红包而来高仿微信:Github 3,515 Star方圆十里:环信编程大赛冠军项目泛聊:定一个小目标写一个QQSlack聊天机器人:一天时间做一个聊天机器人TV视频通话:在电视上视频通话视频通话:Android手机视频通话酷信:ios高仿微信公众号助手:与订阅用户聊天沟通
 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
  查看全部
   这里整理了集成环信的常见问题和一些功能的实现思路,希望能帮助到大家。感谢热心的开发者贡献,大家在观看过程中有不明白的地方欢迎直接跟帖咨询。
 
ios篇

 
Android篇

昵称头像篇

 
直播篇
  1. 一言不合你就搞个直播APP

 
客服集成
  1. IM-SDK和客服SDK并存开发指南—Android篇
  2. IM-SDK和客服SDK并存开发指南—iOS篇

 
开源项目

 
持续更新ing...小伙伴们还有什么想知道欢迎跟帖提出。
 
1
评论

不是群主的好友,能否申请加入该群呢? iOS

微~凉 发表了文章 • 313 次浏览 • 2018-06-05 10:31 • 来自相关话题

不是群主的好友,能否申请加入该群呢?
不是群主的好友,能否申请加入该群呢?
1
评论

iOS 自定义cell iOS cell 自定义cell

不死小强 发表了文章 • 385 次浏览 • 2018-04-10 16:17 • 来自相关话题

简书地址:自定义cell(一)https://www.jianshu.com/p/8befabfdc4f0
               自定义cell(二)https://www.jianshu.com/p/85921a5da8d0










  查看全部
简书地址:自定义cell(一)https://www.jianshu.com/p/8befabfdc4f0
               自定义cell(二)https://www.jianshu.com/p/85921a5da8d0

QQ20180410-161412@2x.png


QQ20180410-161420@2x.png

 
0
评论

环信即时通讯云V3.3.5 SDK已发布,安全传输升级,支持FCM推送 iOS Android 产品更新

产品更新 发表了文章 • 486 次浏览 • 2017-10-23 19:00 • 来自相关话题

10月23日,环信即时通讯云发布了新版本SDK,此次更新的V3.3.5版本增加了传输安全性,AndroidSDK支持FCM推送,FCM是谷歌推出的最新的Android系统级别的消息推送服务(用来替换GCM)。
iOS SDK 更新日志

版本 V3.3.5 2017-10-23

新功能:
增加了传输安全性增加广告插件,可以收集用户信息
优化:
私有部署设置dns的接口优化私有部署重连逻辑限制用户名长度为255需要服务器开通的功能接口返回SERVICE_NOT_ENABLED(505)添加i386库解决模拟器profile时的编译问题
功能修复:
修复4G与wifi切换时偶然出现发送消息失败的bug
 
Android SDK 更新日志

版本 V3.3.5 2017-10-23

新功能:
增加了传输安全性;支持FCM推送;

优化:
私有部署设置dns的接口;优化私有部署重连逻辑;限制用户名长度为255;需要服务器开通的功能接口返回SERVICE_NOT_ENABLED(505);

修复:
修复4G与wifi切换时偶然出现发送消息失败的bug;修复VIVO手机JobService crash问题;
 
版本历史:Android SDK更新日志  ios SDK更新日志
下载地址:SDK下载​ 查看全部
10月23日,环信即时通讯云发布了新版本SDK,此次更新的V3.3.5版本增加了传输安全性,AndroidSDK支持FCM推送,FCM是谷歌推出的最新的Android系统级别的消息推送服务(用来替换GCM)。

iOS SDK 更新日志

版本 V3.3.5 2017-10-23

新功能:

  • 增加了传输安全性
  • 增加广告插件,可以收集用户信息

优化:
  • 私有部署设置dns的接口
  • 优化私有部署重连逻辑
  • 限制用户名长度为255
  • 需要服务器开通的功能接口返回SERVICE_NOT_ENABLED(505)
  • 添加i386库解决模拟器profile时的编译问题

功能修复:
  • 修复4G与wifi切换时偶然出现发送消息失败的bug

 
Android SDK 更新日志

版本 V3.3.5 2017-10-23

新功能:

  • 增加了传输安全性;
  • 支持FCM推送;


优化:
  • 私有部署设置dns的接口;
  • 优化私有部署重连逻辑;
  • 限制用户名长度为255;
  • 需要服务器开通的功能接口返回SERVICE_NOT_ENABLED(505);


修复:
  • 修复4G与wifi切换时偶然出现发送消息失败的bug;
  • 修复VIVO手机JobService crash问题;

 
版本历史:Android SDK更新日志  ios SDK更新日志
下载地址:SDK下载​
0
评论

【环信征文】集成环信,并实现消息免打扰 即时通讯 环信 消息免打扰 IM iOS

cokeyer 发表了文章 • 598 次浏览 • 2017-09-14 11:27 • 来自相关话题

现在大多数社交app都有消息免打扰功能,因为环信SDK主要是对即时通讯模块进行封装,因此如果要实现消息免打扰功能则需要开发者自己对此功能逻辑进行处理。
先解释一下,本人项目中对消息免打扰功能的定义:正常情况下,我们收到的每一条聊天消息都会收到小红点、声音、震动的提示。如果对某个好友设置消息免打扰功能,则只提示小红点,声音和震动则不再提示。

下面简述结合环信SDK时,此功能的实现方法。
环信将即时聊天的所有功能分为四大模块进行管理://聊天模块:
[EMClient sharedClient].chatManager
//好友模块 :
[EMClient sharedClient].contactManager
//群组模块 :
[EMClient sharedClient].groupManager
//聊天室模块:
[EMClient sharedClient].roomManager消息免打扰的功能借助聊天模块[EMClient sharedClient].chatManager的API就能实现。
一般来说,在app内不管当前在哪个界面,只要收到消息都需要被判断是否需要免打扰,因此可以在appdelegate里写如下代码,app通常都有tabBarController,那么也可以让tabBarController来实现如下代码。
首先让控制器tabBarController遵守代理EMChatManagerDelegate
然后成为代理
[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];

EMChatManagerDelegate中有一个代理方法- (void)didReceiveMessages:(NSArray *)aMessages;实现此代理方法- (void)didReceiveMessages:(NSArray *)aMessages{
[self setupUnreadMessageCount];
EMMessage *message = aMessages[0];
NSString *sendPerson = message.from;
BHNavigatiomController *imNaviCV = self.viewControllers[1];
BHConversationListController *converCV = imNaviCV.viewControllers[0];
[converCV refreshDataSource];//只要收到消息就从服务器拿
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
for (NSString *hx_name in AppEngine.IMDataCent.data_ExcuseFriendsData) {
if ([hx_name isEqualToString:sendPerson]) {
return;
}
}
switch (state) {
case UIApplicationStateActive:
[self playSoundAndVibration];
break;
case UIApplicationStateInactive:
[self playSoundAndVibration];
break;
case UIApplicationStateBackground:
[self showNotificationWithMessage:message];
break;
default:
break;
}
}方法中有一个aMessages参数。这个参数是一个消息组,每一个元素都是EMMessage类型的实例。
因为通常都只有一条信息。因此只需要取出EMMessage *message = aMessages[0];
EMMessage类中有许多属性,因此根据一条信息基本可以获取想知道的所有信息。这里我们只需要知道此消息的发送方即可,也就是发送方的环信idNSString *sendPerson = message.from;下面代码中有一AppEngine.IMDataCent.data_ExcuseFriendsData,这个数组里面装的都是已经被设置消息免打扰的好友的环信id,是请求自己服务器获得的,获取的代码最好在一打开app时,就及时获取到。然后根据对好友免打扰设置的操作,访问后台接口进行增删,并刷新数组与后端保持一致即可。
通过[hx_name isEqualToString:sendPerson],遍历免打扰数组与当前消息的发送方环信id,就可以知道是否需要免打扰了。
 
下面附上完整代码//
// BHTabBarController.m
// ShangHeYiYang
//
// Created by LiBohan on 2017/8/24.
// Copyright © 2017年 xxxxx. All rights reserved.
//


//两次提示的默认间隔
static const CGFloat kDefaultPlaySoundInterval = 3.0;
static NSString *kMessageType = @"MessageType";
static NSString *kConversationChatter = @"ConversationChatter";
static NSString *kGroupName = @"GroupName";


#import "BHTabBarController.h"
#import <UserNotifications/UserNotifications.h>
#import "BHConversationListController.h"

@interface BHTabBarController ()<EMChatManagerDelegate,EMContactManagerDelegate>

@property (strong, nonatomic) NSDate *lastPlaySoundDate;

@end

@implementation BHTabBarController

- (void)viewDidLoad {
[super viewDidLoad];

[DemoCallManager sharedManager].mainController = self;

[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];

[[EMClient sharedClient].contactManager addDelegate:self delegateQueue:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupUnreadMessageCount) name:@"setupUnreadMessageCount" object:nil];

}

-(void)friendshipDidRemoveByUser:(NSString *)aUsername{

// __weak __typeof(&*self)weakSelf = self;

dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0/*延迟执行时间*/ * NSEC_PER_SEC));

dispatch_after(delay, dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:contactReloadData object:nil];
});


// if ([AppEngine.mainDataCent.data_UserData.data_HxID isEqualToString:aUsername]) {


//删除好友成功后,再删除聊天会话
[[EMClient sharedClient].chatManager deleteConversation:aUsername isDeleteMessages:YES completion:^(NSString *aConversationId, EMError *aError) {

if (!aError) {
//删除聊天会话,成功后,刷新聊天会话列表
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0/*延迟执行时间*/ * NSEC_PER_SEC));

dispatch_after(delay, dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:talkBtnClickThenUpdateConversionlist object:nil];
});


}

}];
}

// 统计未读消息数
-(void)setupUnreadMessageCount
{
NSArray *conversations = [[EMClient sharedClient].chatManager getAllConversations];
NSInteger unreadCount = 0;
for (EMConversation *conversation in conversations) {
unreadCount += conversation.unreadMessagesCount;
}

NSArray *tabBarItems = self.tabBar.items;

UITabBarItem *conlistTabBarItem = [tabBarItems objectAtIndex:1];
if (unreadCount > 0) {
conlistTabBarItem.badgeValue = [NSString stringWithFormat:@"%i",(int)unreadCount];
}else{
conlistTabBarItem.badgeValue = nil;
}


// UIApplication *application = [UIApplication sharedApplication];
// [application setApplicationIconBadgeNumber:unreadCount];
}


- (void)cmdMessagesDidReceive:(NSArray *)aCmdMessages {
for (EMMessage *message in aCmdMessages) {
EMCmdMessageBody *body = (EMCmdMessageBody *)message.body;
NSLog(@"收到的action是 -- %@",body.action);


if (body.action == nil) {

return;

}

NSData *jsonData = [body.action dataUsingEncoding:NSUTF8StringEncoding];

NSError *err;

NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];

if(err) {

NSLog(@"json解析失败:%@",err);

return;

}

NSNumber *num = dic[@"count"];

NSString *str = [num stringValue];

[AppEngine.IMDataCent requestUpdateUnreadNumberWithUnreadNumber:str];

}


}

- (void)didReceiveMessages:(NSArray *)aMessages{

// [self refresh];

[self setupUnreadMessageCount];

EMMessage *message = aMessages[0];

NSString *sendPerson = message.from;

BHNavigatiomController *imNaviCV = self.viewControllers[1];

BHConversationListController *converCV = imNaviCV.viewControllers[0];

// [converCV refresh];

[converCV refreshDataSource];//只要收到消息就从服务器拿

UIApplicationState state = [[UIApplication sharedApplication] applicationState];

for (NSString *hx_name in AppEngine.IMDataCent.data_ExcuseFriendsData) {

if ([hx_name isEqualToString:sendPerson]) {
return;
}

}

switch (state) {
case UIApplicationStateActive:
[self playSoundAndVibration];
break;
case UIApplicationStateInactive:
[self playSoundAndVibration];
break;
case UIApplicationStateBackground:
[self showNotificationWithMessage:message];
break;
default:
break;
}

}

- (void)playSoundAndVibration{
NSTimeInterval timeInterval = [[NSDate date]
timeIntervalSinceDate:self.lastPlaySoundDate];
if (timeInterval < kDefaultPlaySoundInterval) {
//如果距离上次响铃和震动时间太短, 则跳过响铃
NSLog(@"skip ringing & vibration %@, %@", [NSDate date], self.lastPlaySoundDate);
return;
}

//保存最后一次响铃时间
self.lastPlaySoundDate = [NSDate date];

// 收到消息时,播放音频
[[EMCDDeviceManager sharedInstance] playNewMessageSound];
// 收到消息时,震动
[[EMCDDeviceManager sharedInstance] playVibration];
}


- (void)showNotificationWithMessage:(EMMessage *)message
{
EMPushOptions *options = [[EMClient sharedClient] pushOptions];
NSString *alertBody = nil;
if (options.displayStyle == EMPushDisplayStyleMessageSummary) {
EMMessageBody *messageBody = message.body;
NSString *messageStr = nil;
switch (messageBody.type) {
case EMMessageBodyTypeText:
{
messageStr = ((EMTextMessageBody *)messageBody).text;
}
break;
case EMMessageBodyTypeImage:
{
messageStr = NSLocalizedString(@"message.image", @"Image");
}
break;
case EMMessageBodyTypeLocation:
{
messageStr = NSLocalizedString(@"message.location", @"Location");
}
break;
case EMMessageBodyTypeVoice:
{
messageStr = NSLocalizedString(@"message.voice", @"Voice");
}
break;
case EMMessageBodyTypeVideo:{
messageStr = NSLocalizedString(@"message.video", @"Video");
}
break;
default:
break;
}

do {
// NSString *title = [[UserProfileManager sharedInstance] getNickNameWithUsername:message.from];
NSString *title = @"大佬";

if (message.chatType == EMChatTypeGroupChat) {
NSDictionary *ext = message.ext;
if (ext && ext[kGroupMessageAtList]) {
id target = ext[kGroupMessageAtList];
if ([target isKindOfClass:[NSString class]]) {
if ([kGroupMessageAtAll compare:target options:NSCaseInsensitiveSearch] == NSOrderedSame) {
alertBody = [NSString stringWithFormat:@"%@%@", title, NSLocalizedString(@"group.atPushTitle", @" @ me in the group")];
break;
}
}
else if ([target isKindOfClass:[NSArray class]]) {
NSArray *atTargets = (NSArray*)target;
if ([atTargets containsObject:[EMClient sharedClient].currentUsername]) {
alertBody = [NSString stringWithFormat:@"%@%@", title, NSLocalizedString(@"group.atPushTitle", @" @ me in the group")];
break;
}
}
}
NSArray *groupArray = [[EMClient sharedClient].groupManager getJoinedGroups];
for (EMGroup *group in groupArray) {
if ([group.groupId isEqualToString:message.conversationId]) {
title = [NSString stringWithFormat:@"%@(%@)", message.from, group.subject];
break;
}
}
}
else if (message.chatType == EMChatTypeChatRoom)
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *key = [NSString stringWithFormat:@"OnceJoinedChatrooms_%@", [[EMClient sharedClient] currentUsername]];
NSMutableDictionary *chatrooms = [NSMutableDictionary dictionaryWithDictionary:[ud objectForKey:key]];
NSString *chatroomName = [chatrooms objectForKey:message.conversationId];
if (chatroomName)
{
title = [NSString stringWithFormat:@"%@(%@)", message.from, chatroomName];
}
}

alertBody = [NSString stringWithFormat:@"%@:%@", title, messageStr];
} while (0);
}
else{
// alertBody = NSLocalizedString(@"receiveMessage", @"you have a new message");
alertBody = @"您有一条消息";
}

NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:self.lastPlaySoundDate];
BOOL playSound = NO;
if (!self.lastPlaySoundDate || timeInterval >= kDefaultPlaySoundInterval) {
self.lastPlaySoundDate = [NSDate date];
playSound = YES;
}

NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:[NSNumber numberWithInt:message.chatType] forKey:kMessageType];
[userInfo setObject:message.conversationId forKey:kConversationChatter];

//发送本地推送
if (NSClassFromString(@"UNUserNotificationCenter")) {
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.01 repeats:NO];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
if (playSound) {
content.sound = [UNNotificationSound defaultSound];
}
content.body =alertBody;
content.userInfo = userInfo;
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:message.messageId content:content trigger:trigger];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil];
}
else {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = [NSDate date]; //触发通知的时间
notification.alertBody = alertBody;
notification.alertAction = NSLocalizedString(@"open", @"Open");
notification.timeZone = [NSTimeZone defaultTimeZone];
if (playSound) {
notification.soundName = UILocalNotificationDefaultSoundName;
}
notification.userInfo = userInfo;

//发送通知
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
}

- (void)messagesDidDeliver:(NSArray *)aMessages{

NSLog(@"sf");
}


- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/

@end


 
 
本人github:https://github.com/BHAreslee
本人简书:http://www.jianshu.com/u/bb53043aaa00
以上就是集成环信时暂时发现的问题,欢迎大家分享你们遇到的问题,也欢迎加入QQ群:372251359,一起讨论交流即时通讯的问题。
本人微信公众号:放心安慰剂 查看全部
现在大多数社交app都有消息免打扰功能,因为环信SDK主要是对即时通讯模块进行封装,因此如果要实现消息免打扰功能则需要开发者自己对此功能逻辑进行处理。
先解释一下,本人项目中对消息免打扰功能的定义:正常情况下,我们收到的每一条聊天消息都会收到小红点、声音、震动的提示。如果对某个好友设置消息免打扰功能,则只提示小红点,声音和震动则不再提示。

下面简述结合环信SDK时,此功能的实现方法。
环信将即时聊天的所有功能分为四大模块进行管理:
//聊天模块:
[EMClient sharedClient].chatManager
//好友模块 :
[EMClient sharedClient].contactManager
//群组模块 :
[EMClient sharedClient].groupManager
//聊天室模块:
[EMClient sharedClient].roomManager
消息免打扰的功能借助聊天模块[EMClient sharedClient].chatManager的API就能实现。
一般来说,在app内不管当前在哪个界面,只要收到消息都需要被判断是否需要免打扰,因此可以在appdelegate里写如下代码,app通常都有tabBarController,那么也可以让tabBarController来实现如下代码。
首先让控制器tabBarController遵守代理EMChatManagerDelegate
然后成为代理
[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];

EMChatManagerDelegate中有一个代理方法
- (void)didReceiveMessages:(NSArray *)aMessages;
实现此代理方法
- (void)didReceiveMessages:(NSArray *)aMessages{
[self setupUnreadMessageCount];
EMMessage *message = aMessages[0];
NSString *sendPerson = message.from;
BHNavigatiomController *imNaviCV = self.viewControllers[1];
BHConversationListController *converCV = imNaviCV.viewControllers[0];
[converCV refreshDataSource];//只要收到消息就从服务器拿
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
for (NSString *hx_name in AppEngine.IMDataCent.data_ExcuseFriendsData) {
if ([hx_name isEqualToString:sendPerson]) {
return;
}
}
switch (state) {
case UIApplicationStateActive:
[self playSoundAndVibration];
break;
case UIApplicationStateInactive:
[self playSoundAndVibration];
break;
case UIApplicationStateBackground:
[self showNotificationWithMessage:message];
break;
default:
break;
}
}
方法中有一个aMessages参数。这个参数是一个消息组,每一个元素都是EMMessage类型的实例。
因为通常都只有一条信息。因此只需要取出EMMessage *message = aMessages[0];
EMMessage类中有许多属性,因此根据一条信息基本可以获取想知道的所有信息。这里我们只需要知道此消息的发送方即可,也就是发送方的环信idNSString *sendPerson = message.from;下面代码中有一AppEngine.IMDataCent.data_ExcuseFriendsData,这个数组里面装的都是已经被设置消息免打扰的好友的环信id,是请求自己服务器获得的,获取的代码最好在一打开app时,就及时获取到。然后根据对好友免打扰设置的操作,访问后台接口进行增删,并刷新数组与后端保持一致即可。
通过[hx_name isEqualToString:sendPerson],遍历免打扰数组与当前消息的发送方环信id,就可以知道是否需要免打扰了。
 
下面附上完整代码
//
// BHTabBarController.m
// ShangHeYiYang
//
// Created by LiBohan on 2017/8/24.
// Copyright © 2017年 xxxxx. All rights reserved.
//


//两次提示的默认间隔
static const CGFloat kDefaultPlaySoundInterval = 3.0;
static NSString *kMessageType = @"MessageType";
static NSString *kConversationChatter = @"ConversationChatter";
static NSString *kGroupName = @"GroupName";


#import "BHTabBarController.h"
#import <UserNotifications/UserNotifications.h>
#import "BHConversationListController.h"

@interface BHTabBarController ()<EMChatManagerDelegate,EMContactManagerDelegate>

@property (strong, nonatomic) NSDate *lastPlaySoundDate;

@end

@implementation BHTabBarController

- (void)viewDidLoad {
[super viewDidLoad];

[DemoCallManager sharedManager].mainController = self;

[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];

[[EMClient sharedClient].contactManager addDelegate:self delegateQueue:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupUnreadMessageCount) name:@"setupUnreadMessageCount" object:nil];

}

-(void)friendshipDidRemoveByUser:(NSString *)aUsername{

// __weak __typeof(&*self)weakSelf = self;

dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0/*延迟执行时间*/ * NSEC_PER_SEC));

dispatch_after(delay, dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:contactReloadData object:nil];
});


// if ([AppEngine.mainDataCent.data_UserData.data_HxID isEqualToString:aUsername]) {


//删除好友成功后,再删除聊天会话
[[EMClient sharedClient].chatManager deleteConversation:aUsername isDeleteMessages:YES completion:^(NSString *aConversationId, EMError *aError) {

if (!aError) {
//删除聊天会话,成功后,刷新聊天会话列表
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0/*延迟执行时间*/ * NSEC_PER_SEC));

dispatch_after(delay, dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:talkBtnClickThenUpdateConversionlist object:nil];
});


}

}];
}

// 统计未读消息数
-(void)setupUnreadMessageCount
{
NSArray *conversations = [[EMClient sharedClient].chatManager getAllConversations];
NSInteger unreadCount = 0;
for (EMConversation *conversation in conversations) {
unreadCount += conversation.unreadMessagesCount;
}

NSArray *tabBarItems = self.tabBar.items;

UITabBarItem *conlistTabBarItem = [tabBarItems objectAtIndex:1];
if (unreadCount > 0) {
conlistTabBarItem.badgeValue = [NSString stringWithFormat:@"%i",(int)unreadCount];
}else{
conlistTabBarItem.badgeValue = nil;
}


// UIApplication *application = [UIApplication sharedApplication];
// [application setApplicationIconBadgeNumber:unreadCount];
}


- (void)cmdMessagesDidReceive:(NSArray *)aCmdMessages {
for (EMMessage *message in aCmdMessages) {
EMCmdMessageBody *body = (EMCmdMessageBody *)message.body;
NSLog(@"收到的action是 -- %@",body.action);


if (body.action == nil) {

return;

}

NSData *jsonData = [body.action dataUsingEncoding:NSUTF8StringEncoding];

NSError *err;

NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];

if(err) {

NSLog(@"json解析失败:%@",err);

return;

}

NSNumber *num = dic[@"count"];

NSString *str = [num stringValue];

[AppEngine.IMDataCent requestUpdateUnreadNumberWithUnreadNumber:str];

}


}

- (void)didReceiveMessages:(NSArray *)aMessages{

// [self refresh];

[self setupUnreadMessageCount];

EMMessage *message = aMessages[0];

NSString *sendPerson = message.from;

BHNavigatiomController *imNaviCV = self.viewControllers[1];

BHConversationListController *converCV = imNaviCV.viewControllers[0];

// [converCV refresh];

[converCV refreshDataSource];//只要收到消息就从服务器拿

UIApplicationState state = [[UIApplication sharedApplication] applicationState];

for (NSString *hx_name in AppEngine.IMDataCent.data_ExcuseFriendsData) {

if ([hx_name isEqualToString:sendPerson]) {
return;
}

}

switch (state) {
case UIApplicationStateActive:
[self playSoundAndVibration];
break;
case UIApplicationStateInactive:
[self playSoundAndVibration];
break;
case UIApplicationStateBackground:
[self showNotificationWithMessage:message];
break;
default:
break;
}

}

- (void)playSoundAndVibration{
NSTimeInterval timeInterval = [[NSDate date]
timeIntervalSinceDate:self.lastPlaySoundDate];
if (timeInterval < kDefaultPlaySoundInterval) {
//如果距离上次响铃和震动时间太短, 则跳过响铃
NSLog(@"skip ringing & vibration %@, %@", [NSDate date], self.lastPlaySoundDate);
return;
}

//保存最后一次响铃时间
self.lastPlaySoundDate = [NSDate date];

// 收到消息时,播放音频
[[EMCDDeviceManager sharedInstance] playNewMessageSound];
// 收到消息时,震动
[[EMCDDeviceManager sharedInstance] playVibration];
}


- (void)showNotificationWithMessage:(EMMessage *)message
{
EMPushOptions *options = [[EMClient sharedClient] pushOptions];
NSString *alertBody = nil;
if (options.displayStyle == EMPushDisplayStyleMessageSummary) {
EMMessageBody *messageBody = message.body;
NSString *messageStr = nil;
switch (messageBody.type) {
case EMMessageBodyTypeText:
{
messageStr = ((EMTextMessageBody *)messageBody).text;
}
break;
case EMMessageBodyTypeImage:
{
messageStr = NSLocalizedString(@"message.image", @"Image");
}
break;
case EMMessageBodyTypeLocation:
{
messageStr = NSLocalizedString(@"message.location", @"Location");
}
break;
case EMMessageBodyTypeVoice:
{
messageStr = NSLocalizedString(@"message.voice", @"Voice");
}
break;
case EMMessageBodyTypeVideo:{
messageStr = NSLocalizedString(@"message.video", @"Video");
}
break;
default:
break;
}

do {
// NSString *title = [[UserProfileManager sharedInstance] getNickNameWithUsername:message.from];
NSString *title = @"大佬";

if (message.chatType == EMChatTypeGroupChat) {
NSDictionary *ext = message.ext;
if (ext && ext[kGroupMessageAtList]) {
id target = ext[kGroupMessageAtList];
if ([target isKindOfClass:[NSString class]]) {
if ([kGroupMessageAtAll compare:target options:NSCaseInsensitiveSearch] == NSOrderedSame) {
alertBody = [NSString stringWithFormat:@"%@%@", title, NSLocalizedString(@"group.atPushTitle", @" @ me in the group")];
break;
}
}
else if ([target isKindOfClass:[NSArray class]]) {
NSArray *atTargets = (NSArray*)target;
if ([atTargets containsObject:[EMClient sharedClient].currentUsername]) {
alertBody = [NSString stringWithFormat:@"%@%@", title, NSLocalizedString(@"group.atPushTitle", @" @ me in the group")];
break;
}
}
}
NSArray *groupArray = [[EMClient sharedClient].groupManager getJoinedGroups];
for (EMGroup *group in groupArray) {
if ([group.groupId isEqualToString:message.conversationId]) {
title = [NSString stringWithFormat:@"%@(%@)", message.from, group.subject];
break;
}
}
}
else if (message.chatType == EMChatTypeChatRoom)
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *key = [NSString stringWithFormat:@"OnceJoinedChatrooms_%@", [[EMClient sharedClient] currentUsername]];
NSMutableDictionary *chatrooms = [NSMutableDictionary dictionaryWithDictionary:[ud objectForKey:key]];
NSString *chatroomName = [chatrooms objectForKey:message.conversationId];
if (chatroomName)
{
title = [NSString stringWithFormat:@"%@(%@)", message.from, chatroomName];
}
}

alertBody = [NSString stringWithFormat:@"%@:%@", title, messageStr];
} while (0);
}
else{
// alertBody = NSLocalizedString(@"receiveMessage", @"you have a new message");
alertBody = @"您有一条消息";
}

NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:self.lastPlaySoundDate];
BOOL playSound = NO;
if (!self.lastPlaySoundDate || timeInterval >= kDefaultPlaySoundInterval) {
self.lastPlaySoundDate = [NSDate date];
playSound = YES;
}

NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:[NSNumber numberWithInt:message.chatType] forKey:kMessageType];
[userInfo setObject:message.conversationId forKey:kConversationChatter];

//发送本地推送
if (NSClassFromString(@"UNUserNotificationCenter")) {
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.01 repeats:NO];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
if (playSound) {
content.sound = [UNNotificationSound defaultSound];
}
content.body =alertBody;
content.userInfo = userInfo;
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:message.messageId content:content trigger:trigger];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil];
}
else {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = [NSDate date]; //触发通知的时间
notification.alertBody = alertBody;
notification.alertAction = NSLocalizedString(@"open", @"Open");
notification.timeZone = [NSTimeZone defaultTimeZone];
if (playSound) {
notification.soundName = UILocalNotificationDefaultSoundName;
}
notification.userInfo = userInfo;

//发送通知
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
}

- (void)messagesDidDeliver:(NSArray *)aMessages{

NSLog(@"sf");
}


- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/

@end


 
 
本人github:https://github.com/BHAreslee
本人简书:http://www.jianshu.com/u/bb53043aaa00
以上就是集成环信时暂时发现的问题,欢迎大家分享你们遇到的问题,也欢迎加入QQ群:372251359,一起讨论交流即时通讯的问题。
本人微信公众号:放心安慰剂

qrcode_for_gh_bc92a063b4a2_430.jpg
0
评论

【环信征文】基于环信开发一个医疗APP-Ⅰ iOS 环信 即时通讯 IM

cokeyer 发表了文章 • 510 次浏览 • 2017-09-12 17:56 • 来自相关话题

公司又有了新项目,依然是含有即时通讯功能模块的项目。在经历了上个项目对环信sdk的集成后,对环信EaseUI有了大概的了解。这次果断还是集成环信,一回生二回熟,最主要的还是对环信IM稳定性非常放心!项目医疗类的项目,角色分医生和患者,双方都可主动发起会话,但如果是患者找医生聊天,必须先经过预约,并只能在预约时间区间内才能和医生发送聊天消息、图片、语音、实时音视频。项目基于环信最V3.3.4版本开发,首先在会话界面自定义一个类比如BHChatViewController,继承自EaseMessageViewController类,基本上一个简单的界面就有了。以下就是EaseMessageViewController类的发送各种消息的方法,那么根据需要只需重写以下方法即可。/*!
@method
@brief 发送文本消息
@discussion
@param text 文本消息
@result
*/
- (void)sendTextMessage:(NSString *)text;

/*!
@method
@brief 发送文本消息
@discussion
@param text 文本消息
@param ext 扩展信息
@result
*/
- (void)sendTextMessage:(NSString *)text withExt:(NSDictionary*)ext;

/*!
@method
@brief 发送图片消息
@discussion
@param image 发送图片
@result
*/
- (void)sendImageMessage:(UIImage *)image;

/*!
@method
@brief 发送位置消息
@discussion
@param latitude 经度
@param longitude 纬度
@param address 地址
@result
*/
- (void)sendLocationMessageLatitude:(double)latitude
longitude:(double)longitude
andAddress:(NSString *)address;

/*!
@method
@brief 发送语音消息
@discussion
@param localPath 语音本地地址
@param duration 时长
@result
*/
- (void)sendVoiceMessageWithLocalPath:(NSString *)localPath
duration:(NSInteger)duration;

/*!
@method
@brief 发送视频消息
@discussion
@param url 视频url
@result
*/
- (void)sendVideoMessageWithURL:(NSURL *)url;我在对发送图片的操作进行处理的时候,发现当拍照发图时,走这个方法//当你拍照发图时,走这个方法
- (void)sendImageMessage:(UIImage *)image;但当从相册选择图片发送时,会发现,不走上面的方法了。
仔细检查代码发现走了EaseMessageViewController.m的如下方法





然而这个方法,环信并没有放在EaseMessageViewController.h成为公开方法。我们只需手动粘贴方法到.h,然后在自己的子类重写就可以了。

还有一个关于更多下图更多功能区域的问题





如上图所示,相册、拍照、视频等附加功能按钮,环信用EaseChatBarMoreView类来管理的。
如果需要增加功能按钮用这个方法/*!
@method
@brief 新增一个新的功能按钮
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@result
*/
- (void)insertItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title;移除某个功能按钮用这个方法/*!
@method
@brief 根据索引删除功能按钮
@discussion
@param index 按钮索引
@result
*/
- (void)removeItematIndex:(NSInteger)index;修改一个功能按钮用这个方法/*!
@method
@brief 修改功能按钮图片
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@param index 按钮索引
@result
*/
- (void)updateItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title
atIndex:(NSInteger)index;可当你添加按钮,或者修改按钮时,会发现按钮的名字设置不了
然后检查环信内部的实现,发现title的值在方法里根本就没用到?!
索性不用updateItemWithImage这个方法了,直接去改内部代码。
修改代码如下

到EaseChatBarMoreView.m修改- (void)setupSubviewsForType:(EMChatToolbarType)type方法。改动的部分在代码后面有标注。- (void)setupSubviewsForType:(EMChatToolbarType)type
{
//self.backgroundColor = [UIColor clearColor];
self.accessibilityIdentifier = @"more_view";

_scrollview = [[UIScrollView alloc] init];
_scrollview.pagingEnabled = YES;
_scrollview.showsHorizontalScrollIndicator = NO;
_scrollview.showsVerticalScrollIndicator = NO;
_scrollview.delegate = self;
[self addSubview:_scrollview];

_pageControl = [[UIPageControl alloc] init];
_pageControl.currentPage = 0;
_pageControl.numberOfPages = 1;
[self addSubview:_pageControl];

CGFloat insets = (self.frame.size.width - 4 * CHAT_BUTTON_SIZE) / 5;

_photoButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_photoButton setTitle:@"相册" forState:UIControlStateNormal];//改动
[_photoButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_photoButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_photoButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_photoButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
_photoButton.accessibilityIdentifier = @"image";
[_photoButton setFrame:CGRectMake(insets, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//改动
[_photoButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_photo"] forState:UIControlStateNormal];
[_photoButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_photoSelected"] forState:UIControlStateHighlighted];
[_photoButton addTarget:self action:@selector(photoAction) forControlEvents:UIControlEventTouchUpInside];
_photoButton.tag = MOREVIEW_BUTTON_TAG;
[_scrollview addSubview:_photoButton];

_locationButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_locationButton setTitle:@"位置" forState:UIControlStateNormal];//改动
[_locationButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_locationButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_locationButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_locationButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
_locationButton.accessibilityIdentifier = @"location";
[_locationButton setFrame:CGRectMake(insets * 2 + CHAT_BUTTON_SIZE, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_locationButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_location"] forState:UIControlStateNormal];
[_locationButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_locationSelected"] forState:UIControlStateHighlighted];
[_locationButton addTarget:self action:@selector(locationAction) forControlEvents:UIControlEventTouchUpInside];
_locationButton.tag = MOREVIEW_BUTTON_TAG + 1;
[_scrollview addSubview:_locationButton];

_takePicButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_takePicButton setTitle:@"拍照" forState:UIControlStateNormal];//改动
[_takePicButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_takePicButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_takePicButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_takePicButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_takePicButton setFrame:CGRectMake(insets * 3 + CHAT_BUTTON_SIZE * 2, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//改动
[_takePicButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_camera"] forState:UIControlStateNormal];
[_takePicButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_cameraSelected"] forState:UIControlStateHighlighted];
[_takePicButton addTarget:self action:@selector(takePicAction) forControlEvents:UIControlEventTouchUpInside];
_takePicButton.tag = MOREVIEW_BUTTON_TAG + 2;
_maxIndex = 2;
[_scrollview addSubview:_takePicButton];

CGRect frame = self.frame;
if (type == EMChatToolbarTypeChat) {
frame.size.height = 150;
_audioCallButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_audioCallButton setTitle:@"语音" forState:UIControlStateNormal];//改动
[_audioCallButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_audioCallButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_audioCallButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_audioCallButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_audioCallButton setFrame:CGRectMake(insets * 4 + CHAT_BUTTON_SIZE * 3, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_audioCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_audioCall"] forState:UIControlStateNormal];
[_audioCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_audioCallSelected"] forState:UIControlStateHighlighted];
[_audioCallButton addTarget:self action:@selector(takeAudioCallAction) forControlEvents:UIControlEventTouchUpInside];
_audioCallButton.tag = MOREVIEW_BUTTON_TAG + 3;
[_scrollview addSubview:_audioCallButton];

_videoCallButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_videoCallButton setTitle:@"视频" forState:UIControlStateNormal];//改动
[_videoCallButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_videoCallButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_videoCallButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_videoCallButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_videoCallButton setFrame:CGRectMake(insets, 10 * 2 + CHAT_BUTTON_SIZE + 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_videoCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_videoCall"] forState:UIControlStateNormal];
[_videoCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_videoCallSelected"] forState:UIControlStateHighlighted];
[_videoCallButton addTarget:self action:@selector(takeVideoCallAction) forControlEvents:UIControlEventTouchUpInside];
_videoCallButton.tag =MOREVIEW_BUTTON_TAG + 4;
_maxIndex = 4;
[_scrollview addSubview:_videoCallButton];
}
else if (type == EMChatToolbarTypeGroup)
{
frame.size.height = 80;
}
self.frame = frame;
_scrollview.frame = CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame));
_pageControl.frame = CGRectMake(0, CGRectGetHeight(frame) - 20, CGRectGetWidth(frame), 20);
_pageControl.hidden = _pageControl.numberOfPages<=1;
}




本人github:https://github.com/BHAreslee
本人简书:http://www.jianshu.com/u/bb53043aaa00
以上就是集成环信时暂时发现的问题,欢迎大家分享你们遇到的问题,也欢迎加入QQ群:372251359,一起讨论交流即时通讯的问题。
本人微信公众号:放心安慰剂





 
项目完整源码 查看全部
公司又有了新项目,依然是含有即时通讯功能模块的项目。在经历了上个项目对环信sdk的集成后,对环信EaseUI有了大概的了解。这次果断还是集成环信,一回生二回熟,最主要的还是对环信IM稳定性非常放心!
项目医疗类的项目,角色分医生和患者,双方都可主动发起会话,但如果是患者找医生聊天,必须先经过预约,并只能在预约时间区间内才能和医生发送聊天消息、图片、语音、实时音视频。项目基于环信最V3.3.4版本开发,首先在会话界面自定义一个类比如BHChatViewController,继承自EaseMessageViewController类,基本上一个简单的界面就有了。以下就是EaseMessageViewController类的发送各种消息的方法,那么根据需要只需重写以下方法即可。
/*!
@method
@brief 发送文本消息
@discussion
@param text 文本消息
@result
*/
- (void)sendTextMessage:(NSString *)text;

/*!
@method
@brief 发送文本消息
@discussion
@param text 文本消息
@param ext 扩展信息
@result
*/
- (void)sendTextMessage:(NSString *)text withExt:(NSDictionary*)ext;

/*!
@method
@brief 发送图片消息
@discussion
@param image 发送图片
@result
*/
- (void)sendImageMessage:(UIImage *)image;

/*!
@method
@brief 发送位置消息
@discussion
@param latitude 经度
@param longitude 纬度
@param address 地址
@result
*/
- (void)sendLocationMessageLatitude:(double)latitude
longitude:(double)longitude
andAddress:(NSString *)address;

/*!
@method
@brief 发送语音消息
@discussion
@param localPath 语音本地地址
@param duration 时长
@result
*/
- (void)sendVoiceMessageWithLocalPath:(NSString *)localPath
duration:(NSInteger)duration;

/*!
@method
@brief 发送视频消息
@discussion
@param url 视频url
@result
*/
- (void)sendVideoMessageWithURL:(NSURL *)url;
我在对发送图片的操作进行处理的时候,发现当拍照发图时,走这个方法
//当你拍照发图时,走这个方法
- (void)sendImageMessage:(UIImage *)image;
但当从相册选择图片发送时,会发现,不走上面的方法了。
仔细检查代码发现走了EaseMessageViewController.m的如下方法

环信2.png

然而这个方法,环信并没有放在EaseMessageViewController.h成为公开方法。我们只需手动粘贴方法到.h,然后在自己的子类重写就可以了。

还有一个关于更多下图更多功能区域的问题

EaseChatBarMoreView.png

如上图所示,相册、拍照、视频等附加功能按钮,环信用EaseChatBarMoreView类来管理的。
如果需要增加功能按钮用这个方法
/*!
@method
@brief 新增一个新的功能按钮
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@result
*/
- (void)insertItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title;
移除某个功能按钮用这个方法
/*!
@method
@brief 根据索引删除功能按钮
@discussion
@param index 按钮索引
@result
*/
- (void)removeItematIndex:(NSInteger)index;
修改一个功能按钮用这个方法
/*!
@method
@brief 修改功能按钮图片
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@param index 按钮索引
@result
*/
- (void)updateItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title
atIndex:(NSInteger)index;
可当你添加按钮,或者修改按钮时,会发现按钮的名字设置不了
然后检查环信内部的实现,发现title的值在方法里根本就没用到?!
索性不用updateItemWithImage这个方法了,直接去改内部代码。
修改代码如下

到EaseChatBarMoreView.m修改- (void)setupSubviewsForType:(EMChatToolbarType)type方法。改动的部分在代码后面有标注。
- (void)setupSubviewsForType:(EMChatToolbarType)type
{
//self.backgroundColor = [UIColor clearColor];
self.accessibilityIdentifier = @"more_view";

_scrollview = [[UIScrollView alloc] init];
_scrollview.pagingEnabled = YES;
_scrollview.showsHorizontalScrollIndicator = NO;
_scrollview.showsVerticalScrollIndicator = NO;
_scrollview.delegate = self;
[self addSubview:_scrollview];

_pageControl = [[UIPageControl alloc] init];
_pageControl.currentPage = 0;
_pageControl.numberOfPages = 1;
[self addSubview:_pageControl];

CGFloat insets = (self.frame.size.width - 4 * CHAT_BUTTON_SIZE) / 5;

_photoButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_photoButton setTitle:@"相册" forState:UIControlStateNormal];//改动
[_photoButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_photoButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_photoButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_photoButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
_photoButton.accessibilityIdentifier = @"image";
[_photoButton setFrame:CGRectMake(insets, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//改动
[_photoButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_photo"] forState:UIControlStateNormal];
[_photoButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_photoSelected"] forState:UIControlStateHighlighted];
[_photoButton addTarget:self action:@selector(photoAction) forControlEvents:UIControlEventTouchUpInside];
_photoButton.tag = MOREVIEW_BUTTON_TAG;
[_scrollview addSubview:_photoButton];

_locationButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_locationButton setTitle:@"位置" forState:UIControlStateNormal];//改动
[_locationButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_locationButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_locationButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_locationButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
_locationButton.accessibilityIdentifier = @"location";
[_locationButton setFrame:CGRectMake(insets * 2 + CHAT_BUTTON_SIZE, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_locationButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_location"] forState:UIControlStateNormal];
[_locationButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_locationSelected"] forState:UIControlStateHighlighted];
[_locationButton addTarget:self action:@selector(locationAction) forControlEvents:UIControlEventTouchUpInside];
_locationButton.tag = MOREVIEW_BUTTON_TAG + 1;
[_scrollview addSubview:_locationButton];

_takePicButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_takePicButton setTitle:@"拍照" forState:UIControlStateNormal];//改动
[_takePicButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_takePicButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_takePicButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_takePicButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_takePicButton setFrame:CGRectMake(insets * 3 + CHAT_BUTTON_SIZE * 2, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//改动
[_takePicButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_camera"] forState:UIControlStateNormal];
[_takePicButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_cameraSelected"] forState:UIControlStateHighlighted];
[_takePicButton addTarget:self action:@selector(takePicAction) forControlEvents:UIControlEventTouchUpInside];
_takePicButton.tag = MOREVIEW_BUTTON_TAG + 2;
_maxIndex = 2;
[_scrollview addSubview:_takePicButton];

CGRect frame = self.frame;
if (type == EMChatToolbarTypeChat) {
frame.size.height = 150;
_audioCallButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_audioCallButton setTitle:@"语音" forState:UIControlStateNormal];//改动
[_audioCallButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_audioCallButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_audioCallButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_audioCallButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_audioCallButton setFrame:CGRectMake(insets * 4 + CHAT_BUTTON_SIZE * 3, 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_audioCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_audioCall"] forState:UIControlStateNormal];
[_audioCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_audioCallSelected"] forState:UIControlStateHighlighted];
[_audioCallButton addTarget:self action:@selector(takeAudioCallAction) forControlEvents:UIControlEventTouchUpInside];
_audioCallButton.tag = MOREVIEW_BUTTON_TAG + 3;
[_scrollview addSubview:_audioCallButton];

_videoCallButton =[UIButton buttonWithType:UIButtonTypeCustom];
[_videoCallButton setTitle:@"视频" forState:UIControlStateNormal];//改动
[_videoCallButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];//改动
_videoCallButton.titleLabel.font = [UIFont systemFontOfSize: 12.0];//改动
_videoCallButton.imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 20, 0);//改动
_videoCallButton.titleEdgeInsets = UIEdgeInsetsMake(14, -60, -20, 0);//改动
[_videoCallButton setFrame:CGRectMake(insets, 10 * 2 + CHAT_BUTTON_SIZE + 10, CHAT_BUTTON_SIZE , CHAT_BUTTON_SIZE+10)];//
[_videoCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_videoCall"] forState:UIControlStateNormal];
[_videoCallButton setImage:[UIImage easeImageNamed:@"EaseUIResource.bundle/chatBar_colorMore_videoCallSelected"] forState:UIControlStateHighlighted];
[_videoCallButton addTarget:self action:@selector(takeVideoCallAction) forControlEvents:UIControlEventTouchUpInside];
_videoCallButton.tag =MOREVIEW_BUTTON_TAG + 4;
_maxIndex = 4;
[_scrollview addSubview:_videoCallButton];
}
else if (type == EMChatToolbarTypeGroup)
{
frame.size.height = 80;
}
self.frame = frame;
_scrollview.frame = CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame));
_pageControl.frame = CGRectMake(0, CGRectGetHeight(frame) - 20, CGRectGetWidth(frame), 20);
_pageControl.hidden = _pageControl.numberOfPages<=1;
}




本人github:https://github.com/BHAreslee
本人简书:http://www.jianshu.com/u/bb53043aaa00
以上就是集成环信时暂时发现的问题,欢迎大家分享你们遇到的问题,也欢迎加入QQ群:372251359,一起讨论交流即时通讯的问题。
本人微信公众号:放心安慰剂

qrcode_for_gh_bc92a063b4a2_430.jpg

 
项目完整源码
2
评论

环信进阶篇-实现名片|红包|话题聊天室等自定义cell cell自定义 cell iOS环信 iOS iOS 新手

不死小强 发表了文章 • 4664 次浏览 • 2017-06-20 09:50 • 来自相关话题

    伴随着即时通讯成为了越来越多APP的刚需,匿名社交、阅后即焚、红包等新玩法层出不穷,基本的聊天方式越来越难以满足变态的需求。环信提供的自定义扩展属性功能非常的强大,能够帮助我们在cell中的各种需求做定制处理。这是分享一个之前做过的方法及实现,大家可以借鉴处理的过程及思路,如有不妥之处,请大家及时留言告知,谢谢。 
今天就给大家介绍下怎么对cell中的各种需求的定制处理


 类型一:在现有会话cell上修改UI效果

类似于上面给出的截图,我们有时候需要对环信官方给出的cell进行些许的调整。例如:项目中加入了不同于普通群聊或者聊天室的功能需求
点击话题聊天,大家加入聊天室,这里发出的各种就是不同于普通聊天,普通的聊天只需展示文字、地址、图片等等,但是这里的需求是得加上时间、私聊按钮,没砍需求之前是还有点赞和取消赞的按钮。
我们在普通聊天的基础上新建几个cell,文字、语音、图片、地图等等,不能和原有的普通cell混合起来,因为需求有普通聊天。

直接把普通聊天cell中的代码拷贝过来,再在此基础上进行cell的UI自定义处理,就拿文字聊天时的处理情况为例:

1、拷贝复制原有普通聊天cell内的代码

2、把需要的新增的UI控件初始化

3、适配各类控件

4、传值及赋值

5、新增按钮点击和本身cell的点击效果处理(别和cell上的点击效果混到一起)

6、耐心调整cell上UI效果

以上基本就是简单的自定义cell步骤了,有基础的小伙伴看下步骤应该就有思路了

类型二:类似于红包和名片Cell的UI效果


通常在我们项目中,并不只有文字、图片等等这些简单的聊天内容,有时候我们需要把自己的信息作为一张名片发给对方、发个红包给好朋友、发一个项目中的一个模块介绍给对方等等功能要求。
我们就拿雷哥的这张假名片为例:/*!
@method
@brief 新增一个新的功能按钮
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@result
*/
- (void)insertItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title;
/*!
@method
@brief 修改功能按钮图片
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@param index 按钮索引
@result
*/
- (void)updateItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title
atIndex:(NSInteger)index;
/*!
@method
@brief 根据索引删除功能按钮
@discussion
@param index 按钮索引
@result
*/
- (void)removeItematIndex:(NSInteger)index;*  消息体类型
typedef enum{
EMMessageBodyTypeText  = 1,    /*! \~chinese 文本类型 \~english Text */
EMMessageBodyTypeImage,        /*! \~chinese 图片类型 \~english Image */
EMMessageBodyTypeVideo,        /*! \~chinese 视频类型 \~english Video */
EMMessageBodyTypeLocation,      /*! \~chinese 位置类型 \~english Location */
EMMessageBodyTypeVoice,        /*! \~chinese 语音类型 \~english Voice */
EMMessageBodyTypeFile,          /*! \~chinese 文件类型 \~english File */
EMMessageBodyTypeCmd,          /*! \~chinese 命令类型 \~english Command */
}EMMessageBodyType;
如果环信把这个开放出来,或许我们就更加简单了我们只需自己修改成自己对应的类型即可。但是这个目前就想想,所以我们可以在以上类型中找一个出来,在它的基础上做些文章,变成我们想要的类型。

红包和名片最像什么。。。。对,不就和图片差不多嘛,不过小伙伴也不要以为只能拿图片来做文章,其他的我们都可以拿来用,这里就拿文字类型来作为例子(原理都一样)。




名片类型
这里我们只简要介绍怎么根据会话类型来显示名片,具体传值等怎么做,有基础的小伙伴应该都懂,不懂的小伙伴见文章底部。
 
我们需要在发送名片时,在拓展消息里面存一个名片的字段,这个字段可以被用来判断是名片、红包等等。名片、红包等等中内容,同样也存在拓展属性中(这里不做过多介绍)我们在展示自己的消息和接收到对方的消息时,在文字类型的基础上再进一步判断是什么类型,加载对应类型的视图,如果是红包就加载红包的view,如果是名片就展示名片view......





加载不同类型的cell
好了,以上就是我们所要介绍的两种不同类型cell的处理办法。


以下是补充自定义cell时遇到的各种情况及处理:
1、cell上语音、图片等原始点击和新增按钮点击冲突处理:

注释掉原有的点击方法,把原有的点击方法放到具体的控件上去,避免cell上多个控件点击的冲突

重点:记得把气泡上的点击权限打开_backgroundImageView.userInteractionEnabled = YES;





解决点击冲突
2、cell上语音气泡长度的改变,避免过段影响布局

我们只需把原有语音上的语音长度Label距语音图片控件调大一点距离就能自动把语音类气泡拉长。(其他类型一样处理原理)




语音气泡拉长
3、因新增控件导致在原有cell上高度的变化处理
/*! @method @brief 根据消息的内容,获取当前cell的高度 @discussion @param model 消息对象model @result 返回cell高度 */
+ (CGFloat)cellHeightWithModel:(id)model在原cell高度处理的情况下,根据各种类型的判断进行cell高度的自适应。




cell高度处理
4、文字类型气泡长度的处理

我暂时的处理方法:判断输入的文字长度,加入文字长度小于10,我会在后面自动补全5个空格,被动撑长气泡的长度。

假如小伙伴们有更好的建议也可以留言,谢谢! 查看全部
    伴随着即时通讯成为了越来越多APP的刚需,匿名社交、阅后即焚、红包等新玩法层出不穷,基本的聊天方式越来越难以满足变态的需求。环信提供的自定义扩展属性功能非常的强大,能够帮助我们在cell中的各种需求做定制处理。这是分享一个之前做过的方法及实现,大家可以借鉴处理的过程及思路,如有不妥之处,请大家及时留言告知,谢谢。 
今天就给大家介绍下怎么对cell中的各种需求的定制处理


 类型一:在现有会话cell上修改UI效果

类似于上面给出的截图,我们有时候需要对环信官方给出的cell进行些许的调整。例如:项目中加入了不同于普通群聊或者聊天室的功能需求
点击话题聊天,大家加入聊天室,这里发出的各种就是不同于普通聊天,普通的聊天只需展示文字、地址、图片等等,但是这里的需求是得加上时间、私聊按钮,没砍需求之前是还有点赞和取消赞的按钮。
我们在普通聊天的基础上新建几个cell,文字、语音、图片、地图等等,不能和原有的普通cell混合起来,因为需求有普通聊天。

直接把普通聊天cell中的代码拷贝过来,再在此基础上进行cell的UI自定义处理,就拿文字聊天时的处理情况为例:


1、拷贝复制原有普通聊天cell内的代码

2、把需要的新增的UI控件初始化

3、适配各类控件

4、传值及赋值

5、新增按钮点击和本身cell的点击效果处理(别和cell上的点击效果混到一起)

6、耐心调整cell上UI效果

以上基本就是简单的自定义cell步骤了,有基础的小伙伴看下步骤应该就有思路了


类型二:类似于红包和名片Cell的UI效果


通常在我们项目中,并不只有文字、图片等等这些简单的聊天内容,有时候我们需要把自己的信息作为一张名片发给对方、发个红包给好朋友、发一个项目中的一个模块介绍给对方等等功能要求。
我们就拿雷哥的这张假名片为例:
/*!
@method
@brief 新增一个新的功能按钮
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@result
*/
- (void)insertItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title;
/*!
@method
@brief 修改功能按钮图片
@discussion
@param image 按钮图片
@param highLightedImage 高亮图片
@param title 按钮标题
@param index 按钮索引
@result
*/
- (void)updateItemWithImage:(UIImage*)image
highlightedImage:(UIImage*)highLightedImage
title:(NSString*)title
atIndex:(NSInteger)index;
/*!
@method
@brief 根据索引删除功能按钮
@discussion
@param index 按钮索引
@result
*/
- (void)removeItematIndex:(NSInteger)index;
*  消息体类型
typedef enum{
EMMessageBodyTypeText  = 1,    /*! \~chinese 文本类型 \~english Text */
EMMessageBodyTypeImage,        /*! \~chinese 图片类型 \~english Image */
EMMessageBodyTypeVideo,        /*! \~chinese 视频类型 \~english Video */
EMMessageBodyTypeLocation,      /*! \~chinese 位置类型 \~english Location */
EMMessageBodyTypeVoice,        /*! \~chinese 语音类型 \~english Voice */
EMMessageBodyTypeFile,          /*! \~chinese 文件类型 \~english File */
EMMessageBodyTypeCmd,          /*! \~chinese 命令类型 \~english Command */
}EMMessageBodyType;
如果环信把这个开放出来,或许我们就更加简单了我们只需自己修改成自己对应的类型即可。但是这个目前就想想,所以我们可以在以上类型中找一个出来,在它的基础上做些文章,变成我们想要的类型。

红包和名片最像什么。。。。对,不就和图片差不多嘛,不过小伙伴也不要以为只能拿图片来做文章,其他的我们都可以拿来用,这里就拿文字类型来作为例子(原理都一样)。
01.jpg

名片类型
这里我们只简要介绍怎么根据会话类型来显示名片,具体传值等怎么做,有基础的小伙伴应该都懂,不懂的小伙伴见文章底部。
 
  1. 我们需要在发送名片时,在拓展消息里面存一个名片的字段,这个字段可以被用来判断是名片、红包等等。
  2. 名片、红包等等中内容,同样也存在拓展属性中(这里不做过多介绍)
  3. 我们在展示自己的消息和接收到对方的消息时,在文字类型的基础上再进一步判断是什么类型,加载对应类型的视图,如果是红包就加载红包的view,如果是名片就展示名片view......


02.jpg

加载不同类型的cell
好了,以上就是我们所要介绍的两种不同类型cell的处理办法。


以下是补充自定义cell时遇到的各种情况及处理:
1、cell上语音、图片等原始点击和新增按钮点击冲突处理:

注释掉原有的点击方法,把原有的点击方法放到具体的控件上去,避免cell上多个控件点击的冲突

重点:记得把气泡上的点击权限打开
_backgroundImageView.userInteractionEnabled = YES;


03.jpg

解决点击冲突
2、cell上语音气泡长度的改变,避免过段影响布局

我们只需把原有语音上的语音长度Label距语音图片控件调大一点距离就能自动把语音类气泡拉长。(其他类型一样处理原理)
04.jpg

语音气泡拉长
3、因新增控件导致在原有cell上高度的变化处理
/*! @method @brief 根据消息的内容,获取当前cell的高度 @discussion @param model        消息对象model @result 返回cell高度 */
+ (CGFloat)cellHeightWithModel:(id)model
在原cell高度处理的情况下,根据各种类型的判断进行cell高度的自适应。
05.jpg

cell高度处理
4、文字类型气泡长度的处理

我暂时的处理方法:判断输入的文字长度,加入文字长度小于10,我会在后面自动补全5个空格,被动撑长气泡的长度。

假如小伙伴们有更好的建议也可以留言,谢谢!
0
评论

环信推送的一些常见问题 iOS

木云落 发表了文章 • 7292 次浏览 • 2017-06-07 13:15 • 来自相关话题

原文地址 :  http://blog.csdn.net/jyt199011302/article/details/72829520

参考资料
APNS证书创建和上传到环信后台 : http://www.imgeek.org/article/825308748 
APNS离线推送文档 : http://docs.easemob.com/im/300iosclientintegration/75apns
离线推送的集成代码这里就不一一介绍了, 上面文档中写的 很明白了, 接下来说下比较容易误会的几点
一. 离线推送
如果app集成时添加- (void)applicationDidEnterBackground:(UIApplication *)application {
[[EMClient sharedClient] applicationDidEnterBackground:application];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[[EMClient sharedClient] applicationWillEnterForeground:application];
}
App后台静默后,能够保持长连接3分钟左右。超过3分钟,长连接会断开,当前登录的账号,在服务端被认为离线。消息会存入离线消息空间,之后接收的消息会在再次登录后,连接上服务器,然后通过长连接把消息取走,投递给此用户。如果app配置了推送证书,上传了推送证书并且集成了推送功能,服务器会给接收方发一个APNs推送,则会对离线消息进行APNs推送提示消息内容,通知接收方有一条新消息。
如果想自定义推送的alert,可以在发消息的时候,在消息扩展中添加相应的字段。文档见:APNS内容解析

离线推送 : 当app被杀死或者进入后台三分钟之后
消息回调 : app在前台及app进入后台三分钟之内

注意 : 环信支持推送消息,只是目前还不能根据标签推送给特定用户组,也暂不支持推送模板。CMD消息没有推送,好友请求也没有推送

收不到离线推送时可以从下面几个方面找下原因

1.测试apns推送的时候,接受消息方的app是杀掉状态吗,或者进入后台三分钟以后
2.看看你环信后台上传的证书名称与工程中初始化SDK那里填的证书名 是不是相同的
3.配置证书时候填的id与你工程中的bundle id 是否相同
4.devicetoken有没有传给环信SDK。即查看管理后台中,对应 IM 账户下是否有您刚刚写的证书名。(如果没有,请检查您是否得到了 deviceToken)
5.确认Xcode环境是否配置正确 ,Build Settings---signing,看Debug对应的是不是开发的,Release对应的是不是生产的
6.在确认xcode运行环境是否正确 (Product-->Scheme-->Edit Scheme, 开发证书选Debug,生产证书选Release)
7.证书制作上传过程是否有问题,配置证书的时候是否设置了密码,正确的步骤可以参考:http://www.imgeek.org/article/825308748。另外可以用推送工具进行验证。
8.如果以上都没有问题,可以尝试重新制作上传一下推送证书。
对照这些检查一下,基本上就是这些原因
如果上面几点都符合的话,看下重新登录之后是否可以收到之前收不到推送消息
可以的,话提供一下AppKey,证书名 (查下证书是否被封)以及收不到的推送消息的消息id及发送方和接收方log
log导出请看这篇文章: http://www.imgeek.org/article/825308785 然后转成txt格式上传到工单上,同时注明上述配置都正确 , 环信这边来查下消息推送记录

注意 :后台没有证书名 是指用户列表后面没有显示证书名。这个证书名是SDK初始化的时候传的字符串,用户登录之后会进行绑定。
如果用户没有绑定证书名的话,肯定收不到推送的。这个证书名是用户登录之后绑定的,要确认下初始化SDK的时候有没有传。 options.apnsCertName = apnsCertName;

==============常见问题==============

Q : iOS apns离线推送证书apns的离线推送可以和友盟(极光)推送共用一个证书吗?
A : 环信的推送只要和后台上传的证书对应就可以实现,其他的不关心。
首先苹果推送证书的生成都是统一的方式,这个不区分是极光(友盟)推送证书还是个推证书等等。使用的推送证书只要按照正确的苹果推送证书生成流程创建,都可以使用。
环信添加推送证书可以看http://www.imgeek.org/article/825308748 不是要求必须重新生成推送证书 
Q : 好友申请通知的离线推送?
A : 我们的好友体系,添加好友的申请不支持离线推送。
如果你们是使用App本身的好友体系,可以在app的添加好友业务上向被添加的好友发送文本消息,在EMMessage的ext中设置自定义字段,来区分此条文本消息是否用于好友申请提示,由此来判断处理UI的显示。

Q : iOS的杀死进程远程推送和服务端有关么
A : 如果客户端把远程通知给关了肯定就收不到通知,我们服务器会检测客户端是否有deviceToken,有的话才会把消息发送到deviceToken对应的设备上

Q : 每个项目创建了一个开发的推送证书一个生产的推送证书。这俩证书什么时候要做切换?
A : 在App上传AppStore前需要修改App内初始化SDK设置的推送证书名,EMOptions的apnsCertName。
注意,这里的值需要和在Console管理后天上传时设置的证书名一致。

Q : 绑定devicetoken的时候是否需要先登录到环信?
A : 绑定是需要登录过之后才进行的,
 - (EMError *)bindDeviceToken:(NSData *)aDeviceToken; 是把deviceToken传给SDK。调用登录,SDK会进行绑定。也可以调用 - (void)registerForRemoteNotificationsWithDeviceToken:(NSData *)aDeviceToken
completion:(void (^)(EMError *aError))aCompletionBlock;自己绑定
需要判断是否已经登录,如果已经有登录的账号,再登录会返回 已登录的错误。

Q : 离线推送在客户端怎么设置显示详情?
A : EMPushOptions *pushOptions = [[EMClient sharedClient] pushOptions];
pushOptions.displayStyle = EMPushDisplayStyleMessageSummary;
可以设置离线推送消息显示具体内容还是只显示-您收到一条消息
要设置在登录成功之后,然后要用服务器拉取一遍APNS 属性EMError *error = nil;
EMPushOptions *options = [[EMClient sharedClient] getPushOptionsFromServerWithError:&error];
然后在修改displayStyle

Q : 两个APP通信,如果只希望其中一类APP能收到推送,而另一端的APP不希望收到推送,是不是不希望收到推送的APP不配置证书就好了?
A : 两个App的推送证书都是在同一appkey下单独配置的,如果不希望收到推送,可以对此App不配置推送证书,同时在App代码中注释掉注册远程通知的相关代码。
bundle id对应的证书也可以取消push的功能,针对App不使用任何远程推送服务,包括其他第三方的,如果App还需要其他第三方的推送服务,请忽略这句话。

Q : 不配置推送证书的APP是不是只有刷新的情况下才会显示新的消息?不刷新的情况下APP是看不到新的消息?
A : 不配置推送功能的App,只有在用户登录成功后,才能通过长连接的接收消息回掉中拿到消息体。

Q : 在开发环境下收到了离线推送消息,但是在生成环境下没有收到?
A : 看一下SDK初始化时,是否设置的apnsCerName与生产环境证书上传时填写的证书名一致,还有是否为adhoc打包成ipa文件安装测试的。

Q :发送消息1,2,3,4,5 对方收到推送 2 1 5 顺序不对而且丢失, app角标也不对
A : 1.首先,苹果不保证所有远程推送的到达率。这个可以看苹果官方文档。
Because the delivery of remote notifications is not guaranteed, never include sensitive data or data that can be retrieved by other means in your payload.
2.我们只保证,把离线消息执行远程推送,发给苹果服务器。苹果服务器是否能够百分之百把所有推送送达到指定移动端,这个根据苹果的策略,当 APNs 向你发送了多条推送,你的设备在 APNs 那里下线了,这时 APNs 到你的手机的链路上有多条任务堆积,APNs 的处理方式是,只保留最后一条消息推送给你,然后告知你推送数。那么其他消息会被APNs丢弃。
3.我们保证的是离线消息,当用户重新登录时,可以都接收到。
4. 如果需要找后台查询离线消息(前提接收方已绑定deviceToken)是否成功,需要提供离线消息的messageId,接收方环信id
5.我们的推送角标,是接收方的在服务端的离线消息数。
https://developer.apple.com/li ... 8-SW1
这是官方文档





Q11 :ios是怎么判断离线了 然后发推送的啊 有时候把应用杀掉后 半天收不到推送
A : rest可以查用户的状态,推送前提是此用户有devicetoken已经绑定成功
如果账号所有配置都没问题,杀掉后,其他人发的消息,过几秒就能看到推送
Q12 : Q11不管用会是什么原因呢
A : 配置,还有账号在我们这绑的deviceToken

Q : 多个app共用一个appkey 推送证书怎么配置呢
A : 后台可以上传多套推送证书。

Q :程序关闭后推送了一个消息,点击后怎样获取到环信传过来的数据
A : 需要用户点击横幅后,重新启动App,这时从- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions中,获取字典launchOptions,UIApplicationLaunchOptionsRemoteNotificationKey这个key下的数据,就是aps的字典数据

二 . 消息回调
app的长连接存在的时候,环信服务器检测您为在线状态,是不会给app推送消息的。app端在线的情况下,消息会通过长连接直接收取(didreceivemessage),收到消息,SDK会通过回调通知给上层。app通过收消息的回调拿到消息对象,然后解析并展示UI。
目前我们不支持App切后台后,可以一直执行。
我们SDK在切后台后,实现[[EMClient sharedClient] applicationDidEnterBackground:application];,会保持,直到被系统释放iOS目前其他方式应该都无法去实现一直保持App的活跃状态了。

==============常见问题==============
Q : 本地推送声音设置在哪 ?一条消息推送两声
A : 推送声音设置的要自己实现,具体可参考demo里的ChatDemoHelper类和MainViewController类里的
- (void)showNotificationWithMessage:(EMMessage *)message方法,该方法中有发送本地推送做的一系列操作,本地通知怎么做的,本地通知触发几次,一条消息推送几声,一下接收到多条消息响几声,都需要用户自己实现.

Q : 视频通话,推送怎么实现?
iOS 3.2.3之后,如果在实时通话接收方不在线时,发送提醒。
A : 1.在发起实时音视频通话前,需要设置EMCallOptions对象属性isSendPushIfOffline为YES;
2.遵守协议EMCallBuilderDelegate,实现其中的- (void)callRemoteOffline:(NSString *)aRemoteName 委托方法。
3.在第2步的方法中向 aRemoteName用户发送单聊消息。
如果被叫方已注册远程通知且绑定deviceToken,会收到对应消息的APNs推送,点击横幅来唤醒App。
上面是接收方离线的情况。如果接收方长连接还未断开,只是App切到后台,需要在回调- (void)callDidReceive:(EMCallSession *)aSession中判断当前App是否在后台,如果是弹出本地通知。

Q : EMCallOptions *options = [[EMClient sharedClient].callManager getCallOptions]; //当对方不在线时,是否给对方发送离线消息和推送,并等待对方回应 options.isSendPushIfOffline = YES; [[EMClient sharedClient].callManager setCallOptions:options];isSendPushIfOffline设置为YES后,A用户呼叫B用户,B用户处于离线状态,但B用户没有收到推送。
A : 1. 先确接收方杀掉App后,文本消息是否能收到APNs推送。
2.在1点确认App杀掉可以收到推送前提下,确认实时音视频发送方代码执行顺序如下:(1) EMCallOptions *callOptions = [[EMClient sharedClient].callManager getCallOptions];
callOptions.isSendPushIfOffline = YES;
callOptions.offlineMessageText = @"提示文本";//可选
[[EMClient sharedClient].callManager setCallOptions:callOptions];
(2) callManager调用- (void)startVideoCall:(NSString *)aUsername
completion:(void (^)(EMCallSession *aCallSession, EMError *aError))aCompletionBlock;
或者- (void)startVoiceCall:(NSString *)aUsername
completion:(void (^)(EMCallSession *aCallSession, EMError *aError))aCompletionBlock;

Q : 推送的提示音可不可以自定义啊。
A: 推送的提示音目前不支持自定义,本地通知的你们以自己去设置。
 
Q : app压后台,立刻收到聊天推送来的信息,点击通知栏信息,捕获不到唤起程序事件
A : App切后台后,长连接为断开前,当前弹出的横幅是本地通知,那么此时唤醒时间是本地通知的回调- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;
iOS10后- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler;

Q : 环信在离线状态下能收消息,但是程序运行状态按home按键进入后台的时候无法接受消息,怎么处理
A : App切入后台一段时间内,长连接还未断开,这时候接收消息都是通过SDK的接收消息回调(EMChatManagerDelegate)来收消息,不会执行APNs推送。
如果是需要弹出横幅提醒,需要在接收消息的回调方法中,判断[[UIApplication sharedApplication] applicationState]为UIApplicationStateBackground,然后实现本地通知。可以参考demo中的处理(ChatDemoHelper的- (void)didReceiveMessages:(NSArray *)aMessages)
[attach]7500[/attach][size=13]


[/size] 查看全部
原文地址 :  http://blog.csdn.net/jyt199011302/article/details/72829520

参考资料
APNS证书创建和上传到环信后台 : http://www.imgeek.org/article/825308748 
APNS离线推送文档 : http://docs.easemob.com/im/300iosclientintegration/75apns
离线推送的集成代码这里就不一一介绍了, 上面文档中写的 很明白了, 接下来说下比较容易误会的几点
一. 离线推送
如果app集成时添加
- (void)applicationDidEnterBackground:(UIApplication *)application {
[[EMClient sharedClient] applicationDidEnterBackground:application];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[[EMClient sharedClient] applicationWillEnterForeground:application];
}

App后台静默后,能够保持长连接3分钟左右。超过3分钟,长连接会断开,当前登录的账号,在服务端被认为离线。消息会存入离线消息空间,之后接收的消息会在再次登录后,连接上服务器,然后通过长连接把消息取走,投递给此用户。如果app配置了推送证书,上传了推送证书并且集成了推送功能,服务器会给接收方发一个APNs推送,则会对离线消息进行APNs推送提示消息内容,通知接收方有一条新消息。
如果想自定义推送的alert,可以在发消息的时候,在消息扩展中添加相应的字段。文档见:APNS内容解析

离线推送 : 当app被杀死或者进入后台三分钟之后
消息回调 : app在前台及app进入后台三分钟之内

注意 : 环信支持推送消息,只是目前还不能根据标签推送给特定用户组,也暂不支持推送模板。CMD消息没有推送,好友请求也没有推送

收不到离线推送时可以从下面几个方面找下原因

1.测试apns推送的时候,接受消息方的app是杀掉状态吗,或者进入后台三分钟以后
2.看看你环信后台上传的证书名称与工程中初始化SDK那里填的证书名 是不是相同的
3.配置证书时候填的id与你工程中的bundle id 是否相同
4.devicetoken有没有传给环信SDK。即查看管理后台中,对应 IM 账户下是否有您刚刚写的证书名。(如果没有,请检查您是否得到了 deviceToken)
5.确认Xcode环境是否配置正确 ,Build Settings---signing,看Debug对应的是不是开发的,Release对应的是不是生产的
6.在确认xcode运行环境是否正确 (Product-->Scheme-->Edit Scheme, 开发证书选Debug,生产证书选Release)
7.证书制作上传过程是否有问题,配置证书的时候是否设置了密码,正确的步骤可以参考:http://www.imgeek.org/article/825308748。另外可以用推送工具进行验证。
8.如果以上都没有问题,可以尝试重新制作上传一下推送证书。
对照这些检查一下,基本上就是这些原因
如果上面几点都符合的话,看下重新登录之后是否可以收到之前收不到推送消息
可以的,话提供一下AppKey,证书名 (查下证书是否被封)以及收不到的推送消息的消息id及发送方和接收方log
log导出请看这篇文章: http://www.imgeek.org/article/825308785 然后转成txt格式上传到工单上,同时注明上述配置都正确 , 环信这边来查下消息推送记录

注意 :后台没有证书名 是指用户列表后面没有显示证书名。这个证书名是SDK初始化的时候传的字符串,用户登录之后会进行绑定。
如果用户没有绑定证书名的话,肯定收不到推送的。这个证书名是用户登录之后绑定的,要确认下初始化SDK的时候有没有传。 options.apnsCertName = apnsCertName;

==============常见问题==============

Q : iOS apns离线推送证书apns的离线推送可以和友盟(极光)推送共用一个证书吗?
A : 环信的推送只要和后台上传的证书对应就可以实现,其他的不关心。
首先苹果推送证书的生成都是统一的方式,这个不区分是极光(友盟)推送证书还是个推证书等等。使用的推送证书只要按照正确的苹果推送证书生成流程创建,都可以使用。
环信添加推送证书可以看http://www.imgeek.org/article/825308748 不是要求必须重新生成推送证书 
Q : 好友申请通知的离线推送?
A : 我们的好友体系,添加好友的申请不支持离线推送。
如果你们是使用App本身的好友体系,可以在app的添加好友业务上向被添加的好友发送文本消息,在EMMessage的ext中设置自定义字段,来区分此条文本消息是否用于好友申请提示,由此来判断处理UI的显示。

Q : iOS的杀死进程远程推送和服务端有关么
A : 如果客户端把远程通知给关了肯定就收不到通知,我们服务器会检测客户端是否有deviceToken,有的话才会把消息发送到deviceToken对应的设备上

Q : 每个项目创建了一个开发的推送证书一个生产的推送证书。这俩证书什么时候要做切换?
A : 在App上传AppStore前需要修改App内初始化SDK设置的推送证书名,EMOptions的apnsCertName。
注意,这里的值需要和在Console管理后天上传时设置的证书名一致。

Q : 绑定devicetoken的时候是否需要先登录到环信?
A : 绑定是需要登录过之后才进行的,
 - (EMError *)bindDeviceToken:(NSData *)aDeviceToken; 是把deviceToken传给SDK。调用登录,SDK会进行绑定。也可以调用 - (void)registerForRemoteNotificationsWithDeviceToken:(NSData *)aDeviceToken
completion:(void (^)(EMError *aError))aCompletionBlock;自己绑定
需要判断是否已经登录,如果已经有登录的账号,再登录会返回 已登录的错误。

Q : 离线推送在客户端怎么设置显示详情?
A :
 EMPushOptions *pushOptions = [[EMClient sharedClient] pushOptions];
pushOptions.displayStyle = EMPushDisplayStyleMessageSummary;

可以设置离线推送消息显示具体内容还是只显示-您收到一条消息
要设置在登录成功之后,然后要用服务器拉取一遍APNS 属性
EMError *error = nil;
EMPushOptions *options = [[EMClient sharedClient] getPushOptionsFromServerWithError:&error];

然后在修改displayStyle

Q : 两个APP通信,如果只希望其中一类APP能收到推送,而另一端的APP不希望收到推送,是不是不希望收到推送的APP不配置证书就好了?
A : 两个App的推送证书都是在同一appkey下单独配置的,如果不希望收到推送,可以对此App不配置推送证书,同时在App代码中注释掉注册远程通知的相关代码。
bundle id对应的证书也可以取消push的功能,针对App不使用任何远程推送服务,包括其他第三方的,如果App还需要其他第三方的推送服务,请忽略这句话。

Q : 不配置推送证书的APP是不是只有刷新的情况下才会显示新的消息?不刷新的情况下APP是看不到新的消息?
A : 不配置推送功能的App,只有在用户登录成功后,才能通过长连接的接收消息回掉中拿到消息体。

Q : 在开发环境下收到了离线推送消息,但是在生成环境下没有收到?
A : 看一下SDK初始化时,是否设置的apnsCerName与生产环境证书上传时填写的证书名一致,还有是否为adhoc打包成ipa文件安装测试的。

Q :发送消息1,2,3,4,5 对方收到推送 2 1 5 顺序不对而且丢失, app角标也不对
A : 1.首先,苹果不保证所有远程推送的到达率。这个可以看苹果官方文档。
Because the delivery of remote notifications is not guaranteed, never include sensitive data or data that can be retrieved by other means in your payload.
2.我们只保证,把离线消息执行远程推送,发给苹果服务器。苹果服务器是否能够百分之百把所有推送送达到指定移动端,这个根据苹果的策略,当 APNs 向你发送了多条推送,你的设备在 APNs 那里下线了,这时 APNs 到你的手机的链路上有多条任务堆积,APNs 的处理方式是,只保留最后一条消息推送给你,然后告知你推送数。那么其他消息会被APNs丢弃。
3.我们保证的是离线消息,当用户重新登录时,可以都接收到。
4. 如果需要找后台查询离线消息(前提接收方已绑定deviceToken)是否成功,需要提供离线消息的messageId,接收方环信id
5.我们的推送角标,是接收方的在服务端的离线消息数。
https://developer.apple.com/li ... 8-SW1
这是官方文档
20170605122458543-1.jpeg


Q11 :ios是怎么判断离线了 然后发推送的啊 有时候把应用杀掉后 半天收不到推送
A : rest可以查用户的状态,推送前提是此用户有devicetoken已经绑定成功
如果账号所有配置都没问题,杀掉后,其他人发的消息,过几秒就能看到推送
Q12 : Q11不管用会是什么原因呢
A : 配置,还有账号在我们这绑的deviceToken

Q : 多个app共用一个appkey 推送证书怎么配置呢
A : 后台可以上传多套推送证书。

Q :程序关闭后推送了一个消息,点击后怎样获取到环信传过来的数据
A : 需要用户点击横幅后,重新启动App,这时从- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions中,获取字典launchOptions,UIApplicationLaunchOptionsRemoteNotificationKey这个key下的数据,就是aps的字典数据

二 . 消息回调
app的长连接存在的时候,环信服务器检测您为在线状态,是不会给app推送消息的。app端在线的情况下,消息会通过长连接直接收取(didreceivemessage),收到消息,SDK会通过回调通知给上层。app通过收消息的回调拿到消息对象,然后解析并展示UI。
目前我们不支持App切后台后,可以一直执行。
我们SDK在切后台后,实现[[EMClient sharedClient] applicationDidEnterBackground:application];,会保持,直到被系统释放iOS目前其他方式应该都无法去实现一直保持App的活跃状态了。

==============常见问题==============
Q : 本地推送声音设置在哪 ?一条消息推送两声
A : 推送声音设置的要自己实现,具体可参考demo里的ChatDemoHelper类和MainViewController类里的
- (void)showNotificationWithMessage:(EMMessage *)message方法,该方法中有发送本地推送做的一系列操作,本地通知怎么做的,本地通知触发几次,一条消息推送几声,一下接收到多条消息响几声,都需要用户自己实现.

Q : 视频通话,推送怎么实现?
iOS 3.2.3之后,如果在实时通话接收方不在线时,发送提醒。
A : 1.在发起实时音视频通话前,需要设置EMCallOptions对象属性isSendPushIfOffline为YES;
2.遵守协议EMCallBuilderDelegate,实现其中的- (void)callRemoteOffline:(NSString *)aRemoteName 委托方法。
3.在第2步的方法中向 aRemoteName用户发送单聊消息。
如果被叫方已注册远程通知且绑定deviceToken,会收到对应消息的APNs推送,点击横幅来唤醒App。
上面是接收方离线的情况。如果接收方长连接还未断开,只是App切到后台,需要在回调- (void)callDidReceive:(EMCallSession *)aSession中判断当前App是否在后台,如果是弹出本地通知。

Q : EMCallOptions *options = [[EMClient sharedClient].callManager getCallOptions]; //当对方不在线时,是否给对方发送离线消息和推送,并等待对方回应 options.isSendPushIfOffline = YES; [[EMClient sharedClient].callManager setCallOptions:options];isSendPushIfOffline设置为YES后,A用户呼叫B用户,B用户处于离线状态,但B用户没有收到推送。
A : 1. 先确接收方杀掉App后,文本消息是否能收到APNs推送。
2.在1点确认App杀掉可以收到推送前提下,确认实时音视频发送方代码执行顺序如下:
(1) EMCallOptions *callOptions = [[EMClient sharedClient].callManager getCallOptions];
callOptions.isSendPushIfOffline = YES;
callOptions.offlineMessageText = @"提示文本";//可选
[[EMClient sharedClient].callManager setCallOptions:callOptions];

(2) callManager调用
- (void)startVideoCall:(NSString *)aUsername
completion:(void (^)(EMCallSession *aCallSession, EMError *aError))aCompletionBlock;

或者
- (void)startVoiceCall:(NSString *)aUsername
completion:(void (^)(EMCallSession *aCallSession, EMError *aError))aCompletionBlock;

Q : 推送的提示音可不可以自定义啊。
A: 推送的提示音目前不支持自定义,本地通知的你们以自己去设置。
 
Q : app压后台,立刻收到聊天推送来的信息,点击通知栏信息,捕获不到唤起程序事件
A : App切后台后,长连接为断开前,当前弹出的横幅是本地通知,那么此时唤醒时间是本地通知的回调
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;

iOS10后
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler;

Q : 环信在离线状态下能收消息,但是程序运行状态按home按键进入后台的时候无法接受消息,怎么处理
A : App切入后台一段时间内,长连接还未断开,这时候接收消息都是通过SDK的接收消息回调(EMChatManagerDelegate)来收消息,不会执行APNs推送。
如果是需要弹出横幅提醒,需要在接收消息的回调方法中,判断[[UIApplication sharedApplication] applicationState]为UIApplicationStateBackground,然后实现本地通知。可以参考demo中的处理
(ChatDemoHelper的- (void)didReceiveMessages:(NSArray *)aMessages)
[attach]7500[/attach][size=13]


[/size]
3
评论

IM-SDK和客服SDK并存开发指南—iOS篇 iOS 环信移动客服

beyond 发表了文章 • 5540 次浏览 • 2017-06-06 10:47 • 来自相关话题

一、SDK 介绍
环信客服访客端SDK 基于 IM SDK 3.x , 如果同时使用客服访客端SDK和IM  SDK,只需要在初始化、登录、登出操作时使用客服访客端 SDK 提供的相应 API ,IM 的其他API 均不受影响。UI 部分集成需要分别导入HelpDeskUI 和 EaseUI(也可以自定义UI)。
 下面详细介绍IM 和 客服共存的开发步骤。
二、注意事项
开发过程中 初始化、登录和登出,务必使用客服访客端SDK 的API。IM SDK 和客服SDK 都包括了模拟器的CPU 架构,在上传到app store时需要剔除模拟器的CPU 架构,保留armv7、arm64。
 
三、资源准备
到环信官网下载客服访客端的开源的商城Demo源码 + SDK,下载链接:http://www.easemob.com/download/cs  选择“iOS客服访客端SDK”下载(如下图)。


到环信官网下载IM的开源的Demo源码 + SDK ,下载链接:http://www.easemob.com/download/im 选择iOS SDK(如下图)。  



四、资源简介
只有访客端客服SDK包含视频通话功能,IM SDK不包含从官网下载的客服访客端SDK包括以下目录: kefu-ios-demo、HelpDeskFramework、BaseFramework,HelpDeskUI文件在kefu-ios-demo—>CustomerSystem-ios中分别表示:- helpdeskdemo-ios 为包含实时音视频的Demo,可直接运行。- HelpDeskFramework 为客服访客端SDK,HelpDesk.framework包含实时音视频、HelpDeskLite.framework不包含实时音视频。- HelpDeskUI 为环信用户提供的单聊UI,可在集成的时候视情况使用。- BaseFramework 为客服SDK依赖库(IM SDK)

下载的IM SDK+Demo,包含五个文件夹,并且和客服SDK 中都有 IM 的framework文件,为了保持版本的匹配,我们只使用其中的 EaseUI 而不使用IM SDK中 的framework文件。

五、集成步骤
参考客服访客端SDK文档集成客服的访客端SDK,文档地址:http://docs.easemob.com/cs/300visitoraccess/iossdk。将IM SDK 中的EaseUI 导入到工程中,需要在导入访客端客服SDK的地方导入#import "EaseUI.h"   [重要提示:IM 的初始化、登录、登出操作需要使用客服的相关API]在pch文件中引入
#ifdef __OBJC__
//包含实时音视频功能
#import <HelpDesk/HelpDesk.h>
//若不包含实时音视频,则替换为
#import <HelpDeskLite/HelpDeskLite.h>

#import "HelpDeskUI.h"
#import "EaseUI.h"
#endif
   4. 然后将EaseUI或者HelpDeskUI中的 FixFopen.c文件删除(重复冲突)。
   5. 由于HelpDeskUI 和 EaseUI 中使用了 第三方库,如果工程中出现三方重复的问题,可将HelpDeskUI 和 EaseUI  中的重复文件删除,如果部分接口已经升级或弃用可自行升级、调整。
 
六、工程设置
在General -> Embedded Binaries 中导入HelpDesk.framework和Hyphenate.framework(这是包含实时音视频的,如果不包含实时音视频则导入HelpDeskLite.framework和HyphenateLite.framework)。在Build Settings -> Linking -> Other Linker Flags 中增加 -ObjC项,注意区分大小写。客服 访客端SDK暂不支持bitcode,所以需要将Build Settings -> Build Options -> Enable Bitcode 设为NO。为了减少不必要的警告,可将Build Settings -> Documentation Comments 设为 NO。

提供的兼容Demo介绍:
Demo是在客服的商城Demo上修改,在左上角添加了一个聊天室的按钮,点击按钮会根据appkey随机创建一个账号并登录,登录成功后会进入聊天室列表界面,点击某个聊天室可以在聊天室中聊天。Demo中客服部分功能还是和原商城Demo功能一致。Demo中为了演示因此采用随机注册账号的方式,对于用户场景中,可以先注册好这些账号和自己的账号绑定,这样每次咨询客服就都是同一个人了,也可以显示这个访客曾经的聊天记录。
 
Demo源码地址:下载链接:https://pan.baidu.com/s/1IC3YT ... q.c2c
  查看全部
一、SDK 介绍
  1. 环信客服访客端SDK 基于 IM SDK 3.x , 如果同时使用客服访客端SDK和IM  SDK,只需要在初始化、登录、登出操作时使用客服访客端 SDK 提供的相应 API ,IM 的其他API 均不受影响。
  2. UI 部分集成需要分别导入HelpDeskUI 和 EaseUI(也可以自定义UI)。

 下面详细介绍IM 和 客服共存的开发步骤。
二、注意事项
  1. 开发过程中 初始化、登录和登出,务必使用客服访客端SDK 的API。
  2. IM SDK 和客服SDK 都包括了模拟器的CPU 架构,在上传到app store时需要剔除模拟器的CPU 架构,保留armv7、arm64。

 
三、资源准备
  1. 到环信官网下载客服访客端的开源的商城Demo源码 + SDK,下载链接:http://www.easemob.com/download/cs  选择“iOS客服访客端SDK”下载(如下图)。
    01.jpg
  2. 到环信官网下载IM的开源的Demo源码 + SDK ,下载链接:http://www.easemob.com/download/im 选择iOS SDK(如下图)。  
    02.png

四、资源简介
  1. 只有访客端客服SDK包含视频通话功能,IM SDK不包含
  2. 从官网下载的客服访客端SDK包括以下目录: kefu-ios-demo、HelpDeskFramework、BaseFramework,HelpDeskUI文件在kefu-ios-demo—>CustomerSystem-ios中分别表示:
  3. - helpdeskdemo-ios 为包含实时音视频的Demo,可直接运行。
  4. - HelpDeskFramework 为客服访客端SDK,HelpDesk.framework包含实时音视频、HelpDeskLite.framework不包含实时音视频。
  5. - HelpDeskUI 为环信用户提供的单聊UI,可在集成的时候视情况使用。
  6. - BaseFramework 为客服SDK依赖库(IM SDK)


下载的IM SDK+Demo,包含五个文件夹,并且和客服SDK 中都有 IM 的framework文件,为了保持版本的匹配,我们只使用其中的 EaseUI 而不使用IM SDK中 的framework文件。

五、集成步骤
  1. 参考客服访客端SDK文档集成客服的访客端SDK,文档地址:http://docs.easemob.com/cs/300visitoraccess/iossdk
  2. 将IM SDK 中的EaseUI 导入到工程中,需要在导入访客端客服SDK的地方导入#import "EaseUI.h"   [重要提示:IM 的初始化、登录、登出操作需要使用客服的相关API]
  3. 在pch文件中引入

 #ifdef __OBJC__
//包含实时音视频功能
#import <HelpDesk/HelpDesk.h>
//若不包含实时音视频,则替换为
#import <HelpDeskLite/HelpDeskLite.h>

#import "HelpDeskUI.h"
#import "EaseUI.h"
#endif

   4. 然后将EaseUI或者HelpDeskUI中的 FixFopen.c文件删除(重复冲突)。
   5. 由于HelpDeskUI 和 EaseUI 中使用了 第三方库,如果工程中出现三方重复的问题,可将HelpDeskUI 和 EaseUI  中的重复文件删除,如果部分接口已经升级或弃用可自行升级、调整。
 
六、工程设置
  1. 在General -> Embedded Binaries 中导入HelpDesk.framework和Hyphenate.framework(这是包含实时音视频的,如果不包含实时音视频则导入HelpDeskLite.framework和HyphenateLite.framework)。
  2. 在Build Settings -> Linking -> Other Linker Flags 中增加 -ObjC项,注意区分大小写。
  3. 客服 访客端SDK暂不支持bitcode,所以需要将Build Settings -> Build Options -> Enable Bitcode 设为NO。
  4. 为了减少不必要的警告,可将Build Settings -> Documentation Comments 设为 NO。


提供的兼容Demo介绍:
  1. Demo是在客服的商城Demo上修改,在左上角添加了一个聊天室的按钮,点击按钮会根据appkey随机创建一个账号并登录,登录成功后会进入聊天室列表界面,点击某个聊天室可以在聊天室中聊天。
  2. Demo中客服部分功能还是和原商城Demo功能一致。
  3. Demo中为了演示因此采用随机注册账号的方式,对于用户场景中,可以先注册好这些账号和自己的账号绑定,这样每次咨询客服就都是同一个人了,也可以显示这个访客曾经的聊天记录。

 
Demo源码地址:下载链接:https://pan.baidu.com/s/1IC3YT ... q.c2c
 
3
评论

集成环信遇到的相关问题整理 iOS

木云落 发表了文章 • 7986 次浏览 • 2017-05-22 16:36 • 来自相关话题

最近在整理这段时间被别人问到引入环信可能会出现的问题,记得的也不太多,想到一个就在这里记录一个吧,如果有遇到过本文中没有列出来的,可以问我,我会一一解答的
原文地址: http://blog.csdn.net/jyt199011 ... 83995

1. pod引入的Hyphenate里面的.h文件中和手动下载的sdk相比会缺少Hyphenate.h 。
A :  主要是pod 问题 本地仓库太旧了, 终端行pod repo update, 之后在pod search 'Hyphenate' 如果可以找到3.3.0版本, 就可以下载了 podfile 里面 platform 要指定8.0

2. iOS SDK 从低版本 升到3.3.0 后运行报错 (集成动态库版本报错)
dyld: Library not loaded: @rpath/Hyphenate.framework/Hyphenate
  Referenced from: /Users/white/Library/Developer/CoreSimulator/Devices/BE0DDC26-96AE-4396-A6C5-48DC6938042B/data/Containers/Bundle/Application/4F9F570A-44B5-4F81-AD19-F7AA38D26E40/SYSchoolProject.app/SYSchoolProject
  Reason: image not found




A : 在Build setting -> General这里加上。 还有这里也加上 改不能成optional,
注意 : 改成optional之后会导致初始化为null






3.在AppDelegate中执行[EaseMob sharedInstance]崩溃
A : other link flags添加“-ObjC”选项(注意:O和C大写)


4. pod导入EaseUI 时报错 
A : 先进入Podfile文件中,添加pod 'EaseUI', :git => 'https://github.com/easemob/easeui-ios-hyphenate-cocoapods.git' ,保存退出之后执行pod update即可 ,如果还是失败,可以升级一下pod版本





5.‘Hyphenate/EMSDK.h’ file no found
A : 换下引用#import <HyphenateLite/HyphenateLite.h>
     或者#import <Hyphenate/Hyphenate.h>
     如果此方法不行, 可以试试选中你的项目中的Pods -> EaseUI->Build Phases->Link Binary With Libraries ,点➕->Add Other ,找到工程里面,Pods里面的Hyphenate文件夹下面的Hyphenate.framework 点击open,重新编译就好了






6. 




A :  可以参考问题2的基础上, 再看下相对路径那里


7.集成动态库上传AppStore出现问题, 打包上线时报错
ERROR ITMS-90087: "Unsupported Architectures. The executable for xiantaiApp.app/Frameworks/Hyphenate.framework contains unsupported architectures '[x86_64, i386]'."
A :  遇到这个问题的小伙伴一定是没有认真看咱们环信的官方文档,
由于 iOS 编译的特殊性,为了方便开发者使用,我们将 i386 x86_64 armv7 arm64 几个平台都合并到了一起,所以使用动态库上传appstore时需要将i386 x86_64两个平台删除后,才能正常提交审核

在SDK当前路径下执行以下命令删除i386 x86_64两个平台
实时音视频版本Hyphenate.frameworklipo Hyphenate.framework/Hyphenate -thin armv7 -output Hyphenate_armv7 lipo Hyphenate.framework/Hyphenate -thin arm64 -output Hyphenate_arm64 lipo -create Hyphenate_armv7 Hyphenate_arm64 -output Hyphenate mv Hyphenate Hyphenate.framework/
 
不包含实时音视频版本HyphenateLite.frameworklipo HyphenateLite.framework/HyphenateLite -thin armv7 -output HyphenateLite_armv7 lipo HyphenateLite.framework/HyphenateLite -thin arm64 -output HyphenateLite_arm64 lipo -create HyphenateLite_armv7 HyphenateLite_arm64 -output HyphenateLite mv HyphenateLite HyphenateLite.framework/
拿实时音视频版本版本为例 : 执行完以上命令如图所示




运行完毕后得到的Hyphenate.framework就是最后的结果,拖进工程,编译打包上架。




注意 : 
1. 最后得到的包必须真机编译运行,并且工程要设置编译二进制文件General->Embedded Bunaries.
2. 删除i386、x86_64平台后,SDK会无法支持模拟器编译,只需要在上传AppStore时在进行删除,上传后,替换为删除前的SDK,建议先分别把i386、x86_64、arm64、armv7各平台的包拆分到本地,上传App Store时合并arm64、armv7平台,并移入Hyphenate.framework内。上传后,重新把各平台包合并移入动态库


打包时还有可能报这个错误
ERROR ITMS-90535: "Unexpected CFBundleExecutable Key. The bundle at 'Payload/xiantaiApp.app/EaseUIResource.bundle' does not contain a bundle executable. If this bundle intentionally does not contain an executable, consider removing the CFBundleExecutable key from its Info.plist and using a CFBundlePackageType of BNDL. If this bundle is part of a third-party framework, consider contacting the developer of the framework for an update to address this issue."
A :  ​从EaseUIResource.bundle中找到info.plist删掉CFBundleExecutable,或者整个info.plist删掉



8.ios apns推送是什么原因导致这个错误
注册deviceToken失败:application:didFailToRegisterForRemoteNotificationsWithError: Error Domain=NSCocoaErrorDomain Code=3000 "未找到应用程序的“aps-environment”的授权字符串" UserInfo={NSLocalizedDescription=未找到应用程序的“aps-environment”的授权字符串}
A: 工程配置没有打开推送功能。

9.运行demo报这个错误




A: 没有存储空间了。
 
 
10. SDK3.3.1 以上版本手动导入EaseUI报错
A : 由于demo是用pod集成的,所以直接引入demo中的EaseUI会缺少相关文件,可以直接拖入附件中的EaseUI
如果引入之后报如下图的错误








其实碰到上面这个问题还是很好解决的,这个是因为用到了UIKit里的类,但是只导入了Foundation框架,这个错误在其他类里也会出现,我们可以手动修改Founfation为UIKit,但是我不建议这么做,第一这个做法的工程量比较大, 在其他类里面也要导入,二,不利于移植,当以后环信更新的时候我们还是需要做同样的操作,这里我的做法的创建一个pch文件,在pch文件里面导入UIKit。解决办法:建一个PCH文件在里面添加如下代码:





以上应该会正常了,但是如果集成的是不包含实时音视频的SDK, 您导入的EaseUI不是Lite版的,  那么此时还会报跟第六点一样的错误 , 需要导入EaseUILite 版本,所以会找不到Hyphenate/Hyphenate.h,如果是手动集成,建议在xcode设置一下Build Settings> GCC_PREPROCESSOR_DEFINITIONS >ENABLE_LITE=1,这样easeui就去找HyphenateLite/HyphenateLite.h
也可以通过pod集成,文档上针对easeui集成Full版本和Lite版本sdk特殊的说明http://docs.easemob.com/im/300 ... guide
 
或者不想导入Lite版的 , 只想引入EaseUI 
这时需要把 #import <Hyphenate/Hyphenate.h>注释掉,然后把报错地方的Hyphenate换成HyphenateLite就可以了
 
 
11. 




A : 可以删除或者重命名Podfile.lock文件,重新执行pod install命令 查看全部
最近在整理这段时间被别人问到引入环信可能会出现的问题,记得的也不太多,想到一个就在这里记录一个吧,如果有遇到过本文中没有列出来的,可以问我,我会一一解答的
原文地址: http://blog.csdn.net/jyt199011 ... 83995

1. pod引入的Hyphenate里面的.h文件中和手动下载的sdk相比会缺少Hyphenate.h 。
A :  主要是pod 问题 本地仓库太旧了, 终端行pod repo update, 之后在pod search 'Hyphenate' 如果可以找到3.3.0版本, 就可以下载了 podfile 里面 platform 要指定8.0

2. iOS SDK 从低版本 升到3.3.0 后运行报错 (集成动态库版本报错)
dyld: Library not loaded: @rpath/Hyphenate.framework/Hyphenate
  Referenced from: /Users/white/Library/Developer/CoreSimulator/Devices/BE0DDC26-96AE-4396-A6C5-48DC6938042B/data/Containers/Bundle/Application/4F9F570A-44B5-4F81-AD19-F7AA38D26E40/SYSchoolProject.app/SYSchoolProject
  Reason: image not found
20170330110241533.jpeg

A : 在Build setting -> General这里加上。 还有这里也加上 改不能成optional,
注意 : 改成optional之后会导致初始化为null
20170330104308569.jpeg



3.在AppDelegate中执行[EaseMob sharedInstance]崩溃
A : other link flags添加“-ObjC”选项(注意:O和C大写)


4. pod导入EaseUI 时报错 
A : 先进入Podfile文件中,添加pod 'EaseUI', :git => 'https://github.com/easemob/easeui-ios-hyphenate-cocoapods.git' ,保存退出之后执行pod update即可 ,如果还是失败,可以升级一下pod版本
屏幕快照_2017-05-22_上午10.27_.07_.png


5.‘Hyphenate/EMSDK.h’ file no found
A : 换下引用#import <HyphenateLite/HyphenateLite.h>
     或者#import <Hyphenate/Hyphenate.h>
     如果此方法不行, 可以试试选中你的项目中的Pods -> EaseUI->Build Phases->Link Binary With Libraries ,点➕->Add Other ,找到工程里面,Pods里面的Hyphenate文件夹下面的Hyphenate.framework 点击open,重新编译就好了
20170331200729906.jpeg



6. 
20170331110834145.jpeg

A :  可以参考问题2的基础上, 再看下相对路径那里


7.集成动态库上传AppStore出现问题, 打包上线时报错
ERROR ITMS-90087: "Unsupported Architectures. The executable for xiantaiApp.app/Frameworks/Hyphenate.framework contains unsupported architectures '[x86_64, i386]'."
A :  遇到这个问题的小伙伴一定是没有认真看咱们环信的官方文档,
由于 iOS 编译的特殊性,为了方便开发者使用,我们将 i386 x86_64 armv7 arm64 几个平台都合并到了一起,所以使用动态库上传appstore时需要将i386 x86_64两个平台删除后,才能正常提交审核

在SDK当前路径下执行以下命令删除i386 x86_64两个平台
实时音视频版本Hyphenate.frameworklipo Hyphenate.framework/Hyphenate -thin armv7 -output Hyphenate_armv7 lipo Hyphenate.framework/Hyphenate -thin arm64 -output Hyphenate_arm64 lipo -create Hyphenate_armv7 Hyphenate_arm64 -output Hyphenate mv Hyphenate Hyphenate.framework/
 
不包含实时音视频版本HyphenateLite.frameworklipo HyphenateLite.framework/HyphenateLite -thin armv7 -output HyphenateLite_armv7 lipo HyphenateLite.framework/HyphenateLite -thin arm64 -output HyphenateLite_arm64 lipo -create HyphenateLite_armv7 HyphenateLite_arm64 -output HyphenateLite mv HyphenateLite HyphenateLite.framework/
拿实时音视频版本版本为例 : 执行完以上命令如图所示
20170401112052481.png

运行完毕后得到的Hyphenate.framework就是最后的结果,拖进工程,编译打包上架。
20170401112216045.png

注意 : 
1. 最后得到的包必须真机编译运行,并且工程要设置编译二进制文件General->Embedded Bunaries.
2. 删除i386、x86_64平台后,SDK会无法支持模拟器编译,只需要在上传AppStore时在进行删除,上传后,替换为删除前的SDK,建议先分别把i386、x86_64、arm64、armv7各平台的包拆分到本地,上传App Store时合并arm64、armv7平台,并移入Hyphenate.framework内。上传后,重新把各平台包合并移入动态库


打包时还有可能报这个错误
ERROR ITMS-90535: "Unexpected CFBundleExecutable Key. The bundle at 'Payload/xiantaiApp.app/EaseUIResource.bundle' does not contain a bundle executable. If this bundle intentionally does not contain an executable, consider removing the CFBundleExecutable key from its Info.plist and using a CFBundlePackageType of BNDL. If this bundle is part of a third-party framework, consider contacting the developer of the framework for an update to address this issue."
A :  ​从EaseUIResource.bundle中找到info.plist删掉CFBundleExecutable,或者整个info.plist删掉



8.ios apns推送是什么原因导致这个错误
注册deviceToken失败:application:didFailToRegisterForRemoteNotificationsWithError: Error Domain=NSCocoaErrorDomain Code=3000 "未找到应用程序的“aps-environment”的授权字符串" UserInfo={NSLocalizedDescription=未找到应用程序的“aps-environment”的授权字符串}
A: 工程配置没有打开推送功能。

9.运行demo报这个错误
20170519110027739.png

A: 没有存储空间了。
 
 
10. SDK3.3.1 以上版本手动导入EaseUI报错
A : 由于demo是用pod集成的,所以直接引入demo中的EaseUI会缺少相关文件,可以直接拖入附件中的EaseUI
如果引入之后报如下图的错误
10.1_.png

10.2_.png

其实碰到上面这个问题还是很好解决的,这个是因为用到了UIKit里的类,但是只导入了Foundation框架,这个错误在其他类里也会出现,我们可以手动修改Founfation为UIKit,但是我不建议这么做,第一这个做法的工程量比较大, 在其他类里面也要导入,二,不利于移植,当以后环信更新的时候我们还是需要做同样的操作,这里我的做法的创建一个pch文件,在pch文件里面导入UIKit。解决办法:建一个PCH文件在里面添加如下代码:
10.3_.png


以上应该会正常了,但是如果集成的是不包含实时音视频的SDK, 您导入的EaseUI不是Lite版的,  那么此时还会报跟第六点一样的错误 , 需要导入EaseUILite 版本,所以会找不到Hyphenate/Hyphenate.h,如果是手动集成,建议在xcode设置一下Build Settings> GCC_PREPROCESSOR_DEFINITIONS >ENABLE_LITE=1,这样easeui就去找HyphenateLite/HyphenateLite.h
也可以通过pod集成,文档上针对easeui集成Full版本和Lite版本sdk特殊的说明http://docs.easemob.com/im/300 ... guide
 
或者不想导入Lite版的 , 只想引入EaseUI 
这时需要把 #import <Hyphenate/Hyphenate.h>注释掉,然后把报错地方的Hyphenate换成HyphenateLite就可以了
 
 
11. 
1.png

A : 可以删除或者重命名Podfile.lock文件,重新执行pod install命令
0
评论

环信聊天游客身份和正常用户身份的切换 环信聊天游客身份与正常用户身份的切换 iOS

奋斗的蜗牛 发表了文章 • 730 次浏览 • 2017-05-10 20:39 • 来自相关话题

 
   最近搞环信聊天,需求是游客身份也可以进行聊天,当用户注册了我们的APP后也需要把游客身份切换过来进行聊天,首先我们的环信注册,登录全都放前段处理了,下面就按照我们的需求逻辑来如何切换游客。
 
   1.APP用户的注册,也就注册环信,APP的登录返回的有用户ID,这个时候并没有让他登录环信,只是保存了返回的ID,下面就是用ID来判断该用户是否注册过环信的依据
 
大致说明一下,代码中用到一个类来保证uuid不会改变的状态,为防止app卸载后uuid的改变,我们把他存储到钥匙串里面来保存

下面用图来表示
我们先来看下整个身份切换实现的逻辑图





下面就上代码了,第一步从图中第一步来说判断userID是否存在

这个地方是在点击聊天按钮开始判断的
 -(void)releaseInfo:(UIButton*)sender{
NSString*Hxusername=[userdic objectForKey:@"useid"];//获取保存的userID
NSString*phonestr=  [[NSUserDefaults standardUserDefaults]objectForKey:@"phonenum"];
NSString*chatid=[[phonestr md5String]substringFromIndex:16];//这个是获取客服的欢信ID
//单例里面处理用户是否登录,以及游客随机分配uuid来注册环信IM号
DataManager*datamage= [DataManager shareDataManager];
//判断用户ID是否存在,也就证明是否注册过环信
if (Hxusername.length>0) {
if ([datamage loginKefuSDK])//判断用户是否登录
{//单聊
ChattingViewController *chatController = [[ChattingViewController alloc] initWithConversationChatter:chatid conversationType:EMConversationTypeChat];[self.navigationController pushViewController:chatController animated:YES];
}
}else{
//游客身份的判断
if ([datamage customelogin]) {
//单聊
ChattingViewController *chatController = [[ChattingViewController alloc] initWithConversationChatter:chatid conversationType:EMConversationTypeChat];[self.navigationController pushViewController:chatController animated:YES];
}
}
}上面这是按钮方法里面的数据下面来说,DataManager*datamage= [DataManager shareDataManager];这个单利的方法DataManager.h
@interface DataManager : NSObject
-(BOOL)customelogin;//判断游客之前是否有登录
-(void)requestchattphone;//获取美容院客服聊天的对象电话
@endDataManager.m


@implementation DataManager+(instancetype)shareDataManager{
static DataManager *manager;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[DataManager alloc] init];
});
return manager;
}//userID存在的时候 登录IM
- (BOOL)loginKefuSDK {
NSDictionary*userdic=[[NSUserDefaults standardUserDefaults]objectForKey:@"userMessage"];//接受用户是否登录
NSString*loguser=[NSString stringWithFormat:@"%@",[userdic objectForKey:@"useid"] ];
EMClient *client = [EMClient sharedClient];
//用户已经登录
if (client.isLoggedIn) {
if ([loguser isEqualToString:client.currentUsername])//当前登录用户的ID和即将要登录人的ID是否一样
{
return YES;
}else
{
EMError *error = [[EMClient sharedClient] logout:YES];
if (!error) {
NSLog(@"退出成功");
}
}
}//这里APP用户登录环信的密码统统是123456
EMError *error = [[EMClient sharedClient] loginWithUsername:loguser password:@"123456"];
if (!error) { //IM登录成功
return YES;
} else { //登录失败
NSLog(@"登录失败 error code :%d,error description:%@",error.code,error.errorDescription);
return NO;
}
return NO;
}//游客身份的登录方法
-(BOOL)customelogin
{
EMClient *client = [EMClient sharedClient];
//用户已经登录
if (client.isLoggedIn) {
return YES;
}//该用户没有注册,来用改设备UUID来给用户注册环信,并登录环信
if (![self registerIMuser]) {
return NO;
}
EMError *error = [[EMClient sharedClient] loginWithUsername:self.Hxusername password:@"123456"];
if (!error) { //IM登录成功
return YES;
} else { //登录失败
NSLog(@"登录失败 error code :%d,error description:%@",error.code,error.errorDescription);
return NO;
}
return NO;
}- (BOOL)registerIMuser { //举个栗子。注册建议在服务端创建环信id与自己app的账号一一对应,\
而不要放到APP中,可以在登录自己APP时从返回的结果中获取环信账号再登录环信服务器
EMError *error = nil;
NSString *newUser = [self getrandomUsername];
self.Hxusername = newUser;
error = [[EMClient sharedClient] registerWithUsername:newUser password:@"123456"];
if (error &&  error.code != EMErrorUserAlreadyExist) {
NSLog(@"注册失败;error code:%d,error description :%@",error.code,error.errorDescription);
return NO;
}return YES;
}
//创建一个随机的用户名,这里是设备UUID来代替的​- (NSString *)getrandomUsername {
//第一种方法:
/*NSString *username = nil;
UIDevice *device = [UIDevice currentDevice];//创建设备对象
NSString *deviceUID = [[NSString alloc] initWithString:[[device identifierForVendor] UUIDString]];
if ([deviceUID length] == 0) {
CFUUIDRef uuid = CFUUIDCreate(NULL);
if (uuid)
{
deviceUID = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, uuid);
CFRelease(uuid);
}
username = [deviceUID stringByReplacingOccurrencesOfString:@"-" withString:@""];
username = [username stringByAppendingString:[NSString stringWithFormat:@"%u",arc4random()0000]];
return username;*/
//第二种方法
//加上build ID是为了保证设备的唯一性,如果这里的buildID换了,设备的uuid也会变,这里的解决办法也就是放倒了钥匙串里面,不会因卸载程序,程序升级设备的标识会改变
NSString *SERVICE_NAME = NAVI_TEST_BUNDLE_ID;//最好用程序的bundle id
NSString * str =  [SFHFKeychainUtils getPasswordForUsername:@"UUID" andServiceName:SERVICE_NAME error:nil];  // 从keychain获取数据
if ([str length]<=0)
{
str  = [[[UIDevice currentDevice] identifierForVendor] UUIDString];  // 保存UUID作为手机唯一标识符[SFHFKeychainUtils storeUsername:@"UUID"   andPassword:str    forServiceName:SERVICE_NAME updateExisting:1  error:nil];  // 往keychain添加数据
}
str = [str stringByReplacingOccurrencesOfString:@"-" withString:@""];
return str;
}在这里用到了一个类来处理的UUID不变(APP卸载后不会改变)
SFHFKeychainUtils.h
#import@interface SFHFKeychainUtils : NSObject
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
+ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error;
+ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
@endSFHFKeychainUtils.m​#import "SFHFKeychainUtils.h"
static NSString *SFHFKeychainUtilsErrorDomain = @"SFHFKeychainUtilsErrorDomain";
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR
@interface SFHFKeychainUtils (PrivateMethods)
+ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
@end
#endif
@implementation SFHFKeychainUtils
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error { if (!username || !serviceName) { *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; return nil; }      SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];      if (*error || !item) { return nil; }
// from Advanced Mac OS X Programming, ch. 16
UInt32 length;
char *password;
SecKeychainAttribute attributes[8];
SecKeychainAttributeList list;
attributes[0].tag = kSecAccountItemAttr;
attributes[1].tag = kSecDescriptionItemAttr;
attributes[2].tag = kSecLabelItemAttr;
attributes[3].tag = kSecModDateItemAttr;
list.count = 4;
list.attr = attributes;
OSStatus status = SecKeychainItemCopyContent(item, NULL, &list, &length, (void **)&password);
if (status != noErr)
{
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
return nil;
}
NSString *passwordString = nil;
if (password != NULL)
{ char passwordBuffer[1024];
if (length > 1023) {length = 1023;
}
strncpy(passwordBuffer, password, length);
passwordBuffer[length] = '\0';
passwordString = [NSString stringWithCString:passwordBuffer];
}SecKeychainItemFreeContent(&list, password);CFRelease(item);return passwordString;
}
+ (void) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error {
if (!username || !password || !serviceName) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
return;
}
OSStatus status = noErr;
SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];
if (*error && [*error code] != noErr) {
return;
}
*error = nil;
if (item) {
status = SecKeychainItemModifyAttributesAndData(item,
NULL,
strlen([password UTF8String]),
[password UTF8String]);
CFRelease(item);
}
else {
status = SecKeychainAddGenericPassword(NULL,
strlen([serviceName UTF8String]),
[serviceName UTF8String],
strlen([username UTF8String]),
[username UTF8String],
strlen([password UTF8String]),
[password UTF8String],
NULL);
}
if (status != noErr) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
}
+ (void) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
if (!username || !serviceName) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: 2000 userInfo: nil];
return;
}
*error = nil;
SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];
if (*error && [*error code] != noErr) {
return;
}
OSStatus status;
if (item) {
status = SecKeychainItemDelete(item);
CFRelease(item);
}
if (status != noErr) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
}
+ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
if (!username || !serviceName) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
return nil;
}
*error = nil;
SecKeychainItemRef item;
OSStatus status = SecKeychainFindGenericPassword(NULL,
strlen([serviceName UTF8String]),
[serviceName UTF8String],
strlen([username UTF8String]),
[username UTF8String],
NULL,
NULL,
&item);
if (status != noErr) {
if (status != errSecItemNotFound) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
return nil;
}
return item;
}
#else
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
if (!username || !serviceName) {
if (error != nil) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
}
return nil;
}
if (error != nil) {
*error = nil;
}
// Set up a query dictionary with the base query attributes: item type (generic), username, and service
NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass, kSecAttrAccount, kSecAttrService, nil];
NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword, username, serviceName, nil];
NSMutableDictionary *query = [[NSMutableDictionary alloc] initWithObjects: objects forKeys: keys];
// First do a query for attributes, in case we already have a Keychain item with no password data set.
// One likely way such an incorrect item could have come about is due to the previous (incorrect)
// version of this code (which set the password as a generic attribute instead of password data).
NSMutableDictionary *attributeQuery = [query mutableCopy];
[attributeQuery setObject: (id) kCFBooleanTrue forKey:(__bridge_transfer id) kSecReturnAttributes];
CFTypeRef attrResult = NULL;
OSStatus status = SecItemCopyMatching((__bridge_retained CFDictionaryRef) attributeQuery, &attrResult);
//NSDictionary *attributeResult = (__bridge_transfer NSDictionary *)attrResult;
if (status != noErr) {
// No existing item found--simply return nil for the password
if (error != nil && status != errSecItemNotFound) {
//Only return an error if a real exception happened--not simply for "not found."
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
return nil;
}
// We have an existing item, now query for the password data associated with it.
NSMutableDictionary *passwordQuery = [query mutableCopy];
[passwordQuery setObject: (id) kCFBooleanTrue forKey: (__bridge_transfer id) kSecReturnData];
CFTypeRef resData = NULL;
status = SecItemCopyMatching((__bridge_retained CFDictionaryRef) passwordQuery, (CFTypeRef *) &resData);
NSData *resultData = (__bridge_transfer NSData *)resData;
if (status != noErr) {
if (status == errSecItemNotFound) {
// We found attributes for the item previously, but no password now, so return a special error.
// Users of this API will probably want to detect this error and prompt the user to
// re-enter their credentials.  When you attempt to store the re-entered credentials
// using storeUsername:andPassword:forServiceName:updateExisting:error
// the old, incorrect entry will be deleted and a new one with a properly encrypted
// password will be added.
if (error != nil) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil];
}
}
else {
// Something else went wrong. Simply return the normal Keychain API error code.
if (error != nil) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
}
return nil;
}
NSString *password = nil;
if (resultData) {
password = [[NSString alloc] initWithData: resultData encoding: NSUTF8StringEncoding];
}
else {
// There is an existing item, but we weren't able to get password data for it for some reason,
// Possibly as a result of an item being incorrectly entered by the previous code.
// Set the -1999 error so the code above us can prompt the user again.
if (error != nil) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil];
}
}
return password;
}
+ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error
{
if (!username || !password || !serviceName)
{
if (error != nil)
{
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
}
return NO;
}
// See if we already have a password entered for these credentials.
NSError *getError = nil;
NSString *existingPassword = [SFHFKeychainUtils getPasswordForUsername: username andServiceName: serviceName error:&getError];
if ([getError code] == -1999)
{
// There is an existing entry without a password properly stored (possibly as a result of the previous incorrect version of this code.
// Delete the existing item before moving on entering a correct one.
getError = nil;
[self deleteItemForUsername: username andServiceName: serviceName error: &getError];
if ([getError code] != noErr)
{
if (error != nil)
{
*error = getError;
}
return NO;
}
}
else if ([getError code] != noErr)
{
if (error != nil)
{
*error = getError;
}
return NO;
}
if (error != nil)
{
*error = nil;
}
OSStatus status = noErr;
if (existingPassword)
{
// We have an existing, properly entered item with a password.
// Update the existing item.
if (![existingPassword isEqualToString:password] && updateExisting)
{
//Only update if we're allowed to update existing.  If not, simply do nothing.
NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass,
kSecAttrService,
kSecAttrLabel,
kSecAttrAccount,
nil];
NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword,
serviceName,
serviceName,
username,
nil];
NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys];
status = SecItemUpdate((__bridge_retained CFDictionaryRef) query, (__bridge_retained CFDictionaryRef) [NSDictionary dictionaryWithObject: [password dataUsingEncoding: NSUTF8StringEncoding] forKey: (__bridge_transfer NSString *) kSecValueData]);
}
}
else
{
// No existing entry (or an existing, improperly entered, and therefore now
// deleted, entry).  Create a new entry.
NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass,
kSecAttrService,
kSecAttrLabel,
kSecAttrAccount,
kSecValueData,
nil];
NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword,
serviceName,
serviceName,
username,
[password dataUsingEncoding: NSUTF8StringEncoding],
nil];
NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys];
status = SecItemAdd((__bridge_retained CFDictionaryRef) query, NULL);
}
if (error != nil && status != noErr)
{
// Something went wrong with adding the new item. Return the Keychain error code.
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
return NO;
}
return YES;
}
+ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error
{
if (!username || !serviceName)
{
if (error != nil)
{
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
}
return NO;
}
if (error != nil)
{
*error = nil;
}
NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass, kSecAttrAccount, kSecAttrService, kSecReturnAttributes, nil];
NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword, username, serviceName, kCFBooleanTrue, nil];
NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys];
OSStatus status = SecItemDelete((__bridge_retained CFDictionaryRef) query);
if (error != nil && status != noErr)
{
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
return NO;
}
return YES;
}
#endif
@end 查看全部
 
   最近搞环信聊天,需求是游客身份也可以进行聊天,当用户注册了我们的APP后也需要把游客身份切换过来进行聊天,首先我们的环信注册,登录全都放前段处理了,下面就按照我们的需求逻辑来如何切换游客。
 
   1.APP用户的注册,也就注册环信,APP的登录返回的有用户ID,这个时候并没有让他登录环信,只是保存了返回的ID,下面就是用ID来判断该用户是否注册过环信的依据
 
大致说明一下,代码中用到一个类来保证uuid不会改变的状态,为防止app卸载后uuid的改变,我们把他存储到钥匙串里面来保存

下面用图来表示
我们先来看下整个身份切换实现的逻辑图
4861502-fa2f7d87d00c78d7.jpg


下面就上代码了,第一步从图中第一步来说判断userID是否存在

这个地方是在点击聊天按钮开始判断的
 
-(void)releaseInfo:(UIButton*)sender{
NSString*Hxusername=[userdic objectForKey:@"useid"];//获取保存的userID
NSString*phonestr=  [[NSUserDefaults standardUserDefaults]objectForKey:@"phonenum"];
NSString*chatid=[[phonestr md5String]substringFromIndex:16];//这个是获取客服的欢信ID
//单例里面处理用户是否登录,以及游客随机分配uuid来注册环信IM号
DataManager*datamage= [DataManager shareDataManager];
//判断用户ID是否存在,也就证明是否注册过环信
if (Hxusername.length>0) {
if ([datamage loginKefuSDK])//判断用户是否登录
{//单聊
ChattingViewController *chatController = [[ChattingViewController alloc] initWithConversationChatter:chatid conversationType:EMConversationTypeChat];[self.navigationController pushViewController:chatController animated:YES];
}
}else{
//游客身份的判断
if ([datamage customelogin]) {
//单聊
ChattingViewController *chatController = [[ChattingViewController alloc] initWithConversationChatter:chatid conversationType:EMConversationTypeChat];[self.navigationController pushViewController:chatController animated:YES];
}
}
}上面这是按钮方法里面的数据下面来说,DataManager*datamage= [DataManager shareDataManager];这个单利的方法
DataManager.h
@interface DataManager : NSObject
-(BOOL)customelogin;//判断游客之前是否有登录
-(void)requestchattphone;//获取美容院客服聊天的对象电话
@end
DataManager.m


@implementation DataManager
+(instancetype)shareDataManager{
static DataManager *manager;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[DataManager alloc] init];
});
return manager;
}
//userID存在的时候 登录IM
- (BOOL)loginKefuSDK {
NSDictionary*userdic=[[NSUserDefaults standardUserDefaults]objectForKey:@"userMessage"];//接受用户是否登录
NSString*loguser=[NSString stringWithFormat:@"%@",[userdic objectForKey:@"useid"] ];
EMClient *client = [EMClient sharedClient];
//用户已经登录
if (client.isLoggedIn) {
if ([loguser isEqualToString:client.currentUsername])//当前登录用户的ID和即将要登录人的ID是否一样
{
return YES;
}else
{
EMError *error = [[EMClient sharedClient] logout:YES];
if (!error) {
NSLog(@"退出成功");
}
}
}//这里APP用户登录环信的密码统统是123456
EMError *error = [[EMClient sharedClient] loginWithUsername:loguser password:@"123456"];
if (!error) { //IM登录成功
return YES;
} else { //登录失败
NSLog(@"登录失败 error code :%d,error description:%@",error.code,error.errorDescription);
return NO;
}
return NO;
}
//游客身份的登录方法
-(BOOL)customelogin
{
EMClient *client = [EMClient sharedClient];
//用户已经登录
if (client.isLoggedIn) {
return YES;
}//该用户没有注册,来用改设备UUID来给用户注册环信,并登录环信
if (![self registerIMuser]) {
return NO;
}
EMError *error = [[EMClient sharedClient] loginWithUsername:self.Hxusername password:@"123456"];
if (!error) { //IM登录成功
return YES;
} else { //登录失败
NSLog(@"登录失败 error code :%d,error description:%@",error.code,error.errorDescription);
return NO;
}
return NO;
}
- (BOOL)registerIMuser { //举个栗子。注册建议在服务端创建环信id与自己app的账号一一对应,\
而不要放到APP中,可以在登录自己APP时从返回的结果中获取环信账号再登录环信服务器
EMError *error = nil;
NSString *newUser = [self getrandomUsername];
self.Hxusername = newUser;
error = [[EMClient sharedClient] registerWithUsername:newUser password:@"123456"];
if (error &&  error.code != EMErrorUserAlreadyExist) {
NSLog(@"注册失败;error code:%d,error description :%@",error.code,error.errorDescription);
return NO;
}return YES;
}
//创建一个随机的用户名,这里是设备UUID来代替的​
- (NSString *)getrandomUsername {
//第一种方法:
/*NSString *username = nil;
UIDevice *device = [UIDevice currentDevice];//创建设备对象
NSString *deviceUID = [[NSString alloc] initWithString:[[device identifierForVendor] UUIDString]];
if ([deviceUID length] == 0) {
CFUUIDRef uuid = CFUUIDCreate(NULL);
if (uuid)
{
deviceUID = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, uuid);
CFRelease(uuid);
}
username = [deviceUID stringByReplacingOccurrencesOfString:@"-" withString:@""];
username = [username stringByAppendingString:[NSString stringWithFormat:@"%u",arc4random()0000]];
return username;*/
//第二种方法
//加上build ID是为了保证设备的唯一性,如果这里的buildID换了,设备的uuid也会变,这里的解决办法也就是放倒了钥匙串里面,不会因卸载程序,程序升级设备的标识会改变
NSString *SERVICE_NAME = NAVI_TEST_BUNDLE_ID;//最好用程序的bundle id
NSString * str =  [SFHFKeychainUtils getPasswordForUsername:@"UUID" andServiceName:SERVICE_NAME error:nil];  // 从keychain获取数据
if ([str length]<=0)
{
str  = [[[UIDevice currentDevice] identifierForVendor] UUIDString];  // 保存UUID作为手机唯一标识符[SFHFKeychainUtils storeUsername:@"UUID"   andPassword:str    forServiceName:SERVICE_NAME updateExisting:1  error:nil];  // 往keychain添加数据
}
str = [str stringByReplacingOccurrencesOfString:@"-" withString:@""];
return str;
}在这里用到了一个类来处理的UUID不变(APP卸载后不会改变)
SFHFKeychainUtils.h
#import@interface SFHFKeychainUtils : NSObject
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
+ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error;
+ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
@end
SFHFKeychainUtils.m​
#import "SFHFKeychainUtils.h"
static NSString *SFHFKeychainUtilsErrorDomain = @"SFHFKeychainUtilsErrorDomain";
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR
@interface SFHFKeychainUtils (PrivateMethods)
+ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
@end
#endif
@implementation SFHFKeychainUtils
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error { if (!username || !serviceName) { *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; return nil; }      SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];      if (*error || !item) { return nil; }
// from Advanced Mac OS X Programming, ch. 16
UInt32 length;
char *password;
SecKeychainAttribute attributes[8];
SecKeychainAttributeList list;
attributes[0].tag = kSecAccountItemAttr;
attributes[1].tag = kSecDescriptionItemAttr;
attributes[2].tag = kSecLabelItemAttr;
attributes[3].tag = kSecModDateItemAttr;
list.count = 4;
list.attr = attributes;
OSStatus status = SecKeychainItemCopyContent(item, NULL, &list, &length, (void **)&password);
if (status != noErr)
{
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
return nil;
}
NSString *passwordString = nil;
if (password != NULL)
{ char passwordBuffer[1024];
if (length > 1023) {length = 1023;
}
strncpy(passwordBuffer, password, length);
passwordBuffer[length] = '\0';
passwordString = [NSString stringWithCString:passwordBuffer];
}SecKeychainItemFreeContent(&list, password);CFRelease(item);return passwordString;
}
+ (void) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error {
if (!username || !password || !serviceName) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
return;
}
OSStatus status = noErr;
SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];
if (*error && [*error code] != noErr) {
return;
}
*error = nil;
if (item) {
status = SecKeychainItemModifyAttributesAndData(item,
NULL,
strlen([password UTF8String]),
[password UTF8String]);
CFRelease(item);
}
else {
status = SecKeychainAddGenericPassword(NULL,
strlen([serviceName UTF8String]),
[serviceName UTF8String],
strlen([username UTF8String]),
[username UTF8String],
strlen([password UTF8String]),
[password UTF8String],
NULL);
}
if (status != noErr) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
}
+ (void) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
if (!username || !serviceName) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: 2000 userInfo: nil];
return;
}
*error = nil;
SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];
if (*error && [*error code] != noErr) {
return;
}
OSStatus status;
if (item) {
status = SecKeychainItemDelete(item);
CFRelease(item);
}
if (status != noErr) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
}
+ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
if (!username || !serviceName) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
return nil;
}
*error = nil;
SecKeychainItemRef item;
OSStatus status = SecKeychainFindGenericPassword(NULL,
strlen([serviceName UTF8String]),
[serviceName UTF8String],
strlen([username UTF8String]),
[username UTF8String],
NULL,
NULL,
&item);
if (status != noErr) {
if (status != errSecItemNotFound) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
return nil;
}
return item;
}
#else
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
if (!username || !serviceName) {
if (error != nil) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
}
return nil;
}
if (error != nil) {
*error = nil;
}
// Set up a query dictionary with the base query attributes: item type (generic), username, and service
NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass, kSecAttrAccount, kSecAttrService, nil];
NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword, username, serviceName, nil];
NSMutableDictionary *query = [[NSMutableDictionary alloc] initWithObjects: objects forKeys: keys];
// First do a query for attributes, in case we already have a Keychain item with no password data set.
// One likely way such an incorrect item could have come about is due to the previous (incorrect)
// version of this code (which set the password as a generic attribute instead of password data).
NSMutableDictionary *attributeQuery = [query mutableCopy];
[attributeQuery setObject: (id) kCFBooleanTrue forKey:(__bridge_transfer id) kSecReturnAttributes];
CFTypeRef attrResult = NULL;
OSStatus status = SecItemCopyMatching((__bridge_retained CFDictionaryRef) attributeQuery, &attrResult);
//NSDictionary *attributeResult = (__bridge_transfer NSDictionary *)attrResult;
if (status != noErr) {
// No existing item found--simply return nil for the password
if (error != nil && status != errSecItemNotFound) {
//Only return an error if a real exception happened--not simply for "not found."
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
return nil;
}
// We have an existing item, now query for the password data associated with it.
NSMutableDictionary *passwordQuery = [query mutableCopy];
[passwordQuery setObject: (id) kCFBooleanTrue forKey: (__bridge_transfer id) kSecReturnData];
CFTypeRef resData = NULL;
status = SecItemCopyMatching((__bridge_retained CFDictionaryRef) passwordQuery, (CFTypeRef *) &resData);
NSData *resultData = (__bridge_transfer NSData *)resData;
if (status != noErr) {
if (status == errSecItemNotFound) {
// We found attributes for the item previously, but no password now, so return a special error.
// Users of this API will probably want to detect this error and prompt the user to
// re-enter their credentials.  When you attempt to store the re-entered credentials
// using storeUsername:andPassword:forServiceName:updateExisting:error
// the old, incorrect entry will be deleted and a new one with a properly encrypted
// password will be added.
if (error != nil) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil];
}
}
else {
// Something else went wrong. Simply return the normal Keychain API error code.
if (error != nil) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
}
return nil;
}
NSString *password = nil;
if (resultData) {
password = [[NSString alloc] initWithData: resultData encoding: NSUTF8StringEncoding];
}
else {
// There is an existing item, but we weren't able to get password data for it for some reason,
// Possibly as a result of an item being incorrectly entered by the previous code.
// Set the -1999 error so the code above us can prompt the user again.
if (error != nil) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil];
}
}
return password;
}
+ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error
{
if (!username || !password || !serviceName)
{
if (error != nil)
{
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
}
return NO;
}
// See if we already have a password entered for these credentials.
NSError *getError = nil;
NSString *existingPassword = [SFHFKeychainUtils getPasswordForUsername: username andServiceName: serviceName error:&getError];
if ([getError code] == -1999)
{
// There is an existing entry without a password properly stored (possibly as a result of the previous incorrect version of this code.
// Delete the existing item before moving on entering a correct one.
getError = nil;
[self deleteItemForUsername: username andServiceName: serviceName error: &getError];
if ([getError code] != noErr)
{
if (error != nil)
{
*error = getError;
}
return NO;
}
}
else if ([getError code] != noErr)
{
if (error != nil)
{
*error = getError;
}
return NO;
}
if (error != nil)
{
*error = nil;
}
OSStatus status = noErr;
if (existingPassword)
{
// We have an existing, properly entered item with a password.
// Update the existing item.
if (![existingPassword isEqualToString:password] && updateExisting)
{
//Only update if we're allowed to update existing.  If not, simply do nothing.
NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass,
kSecAttrService,
kSecAttrLabel,
kSecAttrAccount,
nil];
NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword,
serviceName,
serviceName,
username,
nil];
NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys];
status = SecItemUpdate((__bridge_retained CFDictionaryRef) query, (__bridge_retained CFDictionaryRef) [NSDictionary dictionaryWithObject: [password dataUsingEncoding: NSUTF8StringEncoding] forKey: (__bridge_transfer NSString *) kSecValueData]);
}
}
else
{
// No existing entry (or an existing, improperly entered, and therefore now
// deleted, entry).  Create a new entry.
NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass,
kSecAttrService,
kSecAttrLabel,
kSecAttrAccount,
kSecValueData,
nil];
NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword,
serviceName,
serviceName,
username,
[password dataUsingEncoding: NSUTF8StringEncoding],
nil];
NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys];
status = SecItemAdd((__bridge_retained CFDictionaryRef) query, NULL);
}
if (error != nil && status != noErr)
{
// Something went wrong with adding the new item. Return the Keychain error code.
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
return NO;
}
return YES;
}
+ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error
{
if (!username || !serviceName)
{
if (error != nil)
{
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
}
return NO;
}
if (error != nil)
{
*error = nil;
}
NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass, kSecAttrAccount, kSecAttrService, kSecReturnAttributes, nil];
NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword, username, serviceName, kCFBooleanTrue, nil];
NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys];
OSStatus status = SecItemDelete((__bridge_retained CFDictionaryRef) query);
if (error != nil && status != noErr)
{
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
return NO;
}
return YES;
}
#endif
@end
0
评论

炸窝了,苹果禁止使用热更新 iOS 热更新

新闻资讯 发表了文章 • 562 次浏览 • 2017-03-09 11:41 • 来自相关话题

今天一早,不少iOS开发群都炸窝了,原因是部分iOS开发者收到了苹果的警告邮件:




有开发者质疑可能是项目中使用了JSPatch、weex以及ReactNative等热更新技术。对于修复bug提交审核的开发者来说,热更新技术可以帮开发者避免长时间的审核等待以及多次被拒造成的成本开销。但也给黑客留了后门,也就违反了苹果的安全和隐私政策。

不过这次苹果只是对使用热更新的应用进行了警告,并没有开发者反应产品因此问题被下架。

对此,开发者表示:

舞小月:苹果注重的就是流畅性和用户体验,混编做的东西肯定没有native的流畅,这就违背了苹果本来的意愿,被禁也是正常的,而且苹果自己的蛋糕为何要分给竞争对手?以前没混编的时候你该怎么做不还是做了,现在没有,不代表以后没有,就像之前没有混编,后来有了混编。新的框架苹果自然也会去完善,苹果既然做了这个决定,他肯定会优化自己的东西。

Gilbertat:苹果爸爸会不会在自己的生态中搞死js啊

luohui8891:我们也是昨天收到的,目前没有什么对策。我们的APP只是用JSPatch做热修复,并不修改应用的功能行为等(但我觉得Apple并不care这个)。

lsllsllsl:没用RN没用JSPatch,同样收到警告。

luohui8891:@tcathy 根据邮件里说是你下次提交前请去掉这样远程下载代码运行的机制。所以应该就是下个版本如果不删除就reject

Loooren:早上收到邮件,itunesconnect站内信,电话通知....用到了weex

xiaofuyesnew:昨天晚上微软发布了Visual Studio 2017,自带基于React Native的iOS开发功能。鉴于微软这两年来开源的力度,发布这一功能似乎是在抢占开发者的市场,基于vs2017,在非苹果上开发ios应用更容易了。所以,苹果在这个节骨眼发出这个警告邮件,就有点威胁现有开发者的意思。暗地里想跟微软互怼。

对于那些已经在学习RN、weex、JSPatch的同学来说,这是个悲惨的故事









从苹果的角度看,禁止应用使用热更新技术更多是为了保护用户隐私、数据安全以及其全力打造的生态圈。对于用户来说,出于安全起见,应谨慎授予应用权限;对于开发者来说,为了审核以及长远的用户体验考虑,不要轻易触碰苹果拉的那条红线。




以上内容来源于CocoaChina,GitHub 查看全部
今天一早,不少iOS开发群都炸窝了,原因是部分iOS开发者收到了苹果的警告邮件:
001.png

有开发者质疑可能是项目中使用了JSPatch、weex以及ReactNative等热更新技术。对于修复bug提交审核的开发者来说,热更新技术可以帮开发者避免长时间的审核等待以及多次被拒造成的成本开销。但也给黑客留了后门,也就违反了苹果的安全和隐私政策。

不过这次苹果只是对使用热更新的应用进行了警告,并没有开发者反应产品因此问题被下架。

对此,开发者表示:

舞小月:苹果注重的就是流畅性和用户体验,混编做的东西肯定没有native的流畅,这就违背了苹果本来的意愿,被禁也是正常的,而且苹果自己的蛋糕为何要分给竞争对手?以前没混编的时候你该怎么做不还是做了,现在没有,不代表以后没有,就像之前没有混编,后来有了混编。新的框架苹果自然也会去完善,苹果既然做了这个决定,他肯定会优化自己的东西。

Gilbertat:苹果爸爸会不会在自己的生态中搞死js啊

luohui8891:我们也是昨天收到的,目前没有什么对策。我们的APP只是用JSPatch做热修复,并不修改应用的功能行为等(但我觉得Apple并不care这个)。

lsllsllsl:没用RN没用JSPatch,同样收到警告。

luohui8891:@tcathy 根据邮件里说是你下次提交前请去掉这样远程下载代码运行的机制。所以应该就是下个版本如果不删除就reject

Loooren:早上收到邮件,itunesconnect站内信,电话通知....用到了weex

xiaofuyesnew:昨天晚上微软发布了Visual Studio 2017,自带基于React Native的iOS开发功能。鉴于微软这两年来开源的力度,发布这一功能似乎是在抢占开发者的市场,基于vs2017,在非苹果上开发ios应用更容易了。所以,苹果在这个节骨眼发出这个警告邮件,就有点威胁现有开发者的意思。暗地里想跟微软互怼。

对于那些已经在学习RN、weex、JSPatch的同学来说,这是个悲惨的故事
002.png


003.png

从苹果的角度看,禁止应用使用热更新技术更多是为了保护用户隐私、数据安全以及其全力打造的生态圈。对于用户来说,出于安全起见,应谨慎授予应用权限;对于开发者来说,为了审核以及长远的用户体验考虑,不要轻易触碰苹果拉的那条红线。
004.png

以上内容来源于CocoaChinaGitHub
3
评论

Android ios V3.3.0 SDK 已发布,增加群组、聊天室管理员权限 iOS Android 产品更新

产品更新 发表了文章 • 882 次浏览 • 2017-03-08 16:37 • 来自相关话题

 Android​ V3.3.0 2017-03-07
 
新功能:
群组和聊天室改造:增加管理员权限,新增禁言,增减管理员的功能,支持使用分批的方式获取成员,禁言,管理员列表,支持完善的聊天室功能。新增加API请查看链接3.3.0 api修改优化dns劫持时的处理增加EMConversation.latestMessageFromOthers,表示收到对方的最后一条消息增加EMClient.compressLogs,压缩log,Demo中增加通过邮件发送log的示例libs.without.audio继续支持armeabi,解决armeabi-v5te的支持问题

bug 修订:
修复2.x升级3.x消息未读数为0的bugDemo在视频通话时,主叫方铃声没有播放的问题Demo在视频通话时,主叫方在建立连接成功后,文字提示不正确Demo在聊天窗口界面,清空消息后,收到新的消息,返回会话列表,未读消息数显示不正确修复在Oppo和Vivo手机上出现的JobService报错。EMGroupManager.createGroup成员列表数超过512产生的overflow错误修复部分手机在网络切换时发消息慢的bug
 
ios V3.3.0 2017-03-07
 
新功能:
新增:群组改造,增加一系列新接口,具体查看iOS iOS 3.3.0 api修改新增:获取SDK日志路径接口,将日志文件压缩成.gz文件,返回gz文件路径,[EMClient getLogFilesPath:]更新:使用视频通话录制功能时,必须在开始通话之前调用[EMVideoRecorderPlugin initGlobalConfig]

优化:
优化DNS劫持时的处理切换网络时,减小消息重发的等待时间

修复:
音视频通话丢包率(以前返回的是丢包数)IOS动态库用H264编码在iPhone6s上崩溃实时音视频新旧版互通崩溃
 
版本历史:Android SDK更新日志  ios SDK更新日志
下载地址:SDK下载 查看全部

7658.jpg_wh860_.jpg

 Android​ V3.3.0 2017-03-07
 
新功能:
  1. 群组和聊天室改造:增加管理员权限,新增禁言,增减管理员的功能,支持使用分批的方式获取成员,禁言,管理员列表,支持完善的聊天室功能。新增加API请查看链接3.3.0 api修改
  2. 优化dns劫持时的处理
  3. 增加EMConversation.latestMessageFromOthers,表示收到对方的最后一条消息
  4. 增加EMClient.compressLogs,压缩log,Demo中增加通过邮件发送log的示例
  5. libs.without.audio继续支持armeabi,解决armeabi-v5te的支持问题


bug 修订:
  1. 修复2.x升级3.x消息未读数为0的bug
  2. Demo在视频通话时,主叫方铃声没有播放的问题
  3. Demo在视频通话时,主叫方在建立连接成功后,文字提示不正确
  4. Demo在聊天窗口界面,清空消息后,收到新的消息,返回会话列表,未读消息数显示不正确
  5. 修复在Oppo和Vivo手机上出现的JobService报错。
  6. EMGroupManager.createGroup成员列表数超过512产生的overflow错误
  7. 修复部分手机在网络切换时发消息慢的bug

 
ios V3.3.0 2017-03-07
 
新功能:
  1. 新增:群组改造,增加一系列新接口,具体查看iOS iOS 3.3.0 api修改
  2. 新增:获取SDK日志路径接口,将日志文件压缩成.gz文件,返回gz文件路径,[EMClient getLogFilesPath:]
  3. 更新:使用视频通话录制功能时,必须在开始通话之前调用[EMVideoRecorderPlugin initGlobalConfig]


优化:
  1. 优化DNS劫持时的处理
  2. 切换网络时,减小消息重发的等待时间


修复:
  1. 音视频通话丢包率(以前返回的是丢包数)
  2. IOS动态库用H264编码在iPhone6s上崩溃
  3. 实时音视频新旧版互通崩溃

 
版本历史:Android SDK更新日志  ios SDK更新日志
下载地址:SDK下载
1
评论

淘宝购物车界面背后的逻辑及实现源码,欢迎Star! iOS

不死小强 发表了文章 • 4499 次浏览 • 2017-02-22 17:16 • 来自相关话题

ViewController: 购物车界面
整个界面就是TableView + 底部结账栏View组成





以店铺为section:商店下的商品为row和店铺名称组成一个 section

定制段头的View 把section的全选按钮、点击商品、编辑的三个按钮的方法用代理的方法。

-(UIView*)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section;





建议使用Masonry进行cell适配

cell的创建就是和我们平常的一样,把要展示的样式代码编写或者xib都可以。再把数据源填充到我们所创建好的cell中和段头上。
创建好一个View添加在TableView的下方。View上写上全选及总金额等UI。每次我们选定的物品的增减都要调用该View赋值的方法,刷新金额等字段显示。





Cell:物品栏

创建两种cell,一个是正常的物品显示cell,另一个cell是编辑后的cell。

正常的cell:只说下label中划线的实现
//中划线


NSDictionary *attribtDic = @{NSStrikethroughStyleAttributeName: [NSNumber numberWithInteger:NSUnderlineStyleSingle]};

NSMutableAttributedString *attribtStr = [[NSMutableAttributedString alloc]initWithString:info[@"GoodsOldPrice"] attributes:attribtDic];

// 赋值

_Goods_OldPrice.attributedText = attribtStr;

编辑后的cell:

主要由三部分组成。商品数量 + 商品种类 + 删除




商品数量用的是第三方PPNumberButton,点击时改变model中该商品的实际数量;点击商品种类进行重新选择(方法未实现,原理一样);点击删除进行cell的删除及数据源的删除。

注释:这里的删除 及 修改 都是要对数据源进行修改在刷新的

Model:数据源的处理及购物车内各类按钮的判断

demo中的数据源我没有放到model中去处理,其实原理都一样,我把判断各类按钮的判断字段加到数据源中去了,如果用model模型的话,自己加上相对应的字段,并设置初始值。当进行model数据源的修改时,直接进行修改。





这是一种model处理方式,还有一种就是用JsonModel来处理,一层层的写下来,原理一样。 
购物车逻辑及实现总结

逻辑整理:当我们把有购买意向的物品加到购物车后,我们在购物车中调用接口获取购物车中的物品信息。数据源格式大概是(感觉不怎么对,但是能理解就行)

[

{@“店铺信息”:[@{物品信息},@{物品信息},@{物品信息}]},  -------》组一

{@"店铺信息":[@{物品信息}]},                                              -------》组二

{@”店铺信息“:[@{物品信息},@{物品信息}]}                        -------》组三

]

把数据源用model装起来,把数据填充到tableview中去。

1.单个商品的选择、单个店铺内所有商品的选择、结账栏下的全选                      

如果做得很简单的话,可以直接用系统的单选和全选方法。

最重要的两句 !!!!

TableDemo.editing=YES;      编辑状态

TableDemo.allowsMultipleSelectionDuringEditing=YES;   编辑的时候多选


cell.tintColor= [UIColorredColor];   选中后的颜色


选中和取消选中


-(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath    选中


-(void)tableView:(UITableView*)tableView didDeselectRowAtIndexPath:(NSIndexPath*)indexPath    取消选中





如果不用系统的话,则利用model来进行单选和全选的操作,选中和取消选中对model中的判断字段进行修改,刷新当前cell或者section。

//一个section刷新


NSIndexSet *indexSet=[[NSIndexSet alloc]initWithIndex:section];


[tableview reloadSections:indexSet


withRowAnimation:UITableViewRowAnimationAutomatic];


//一个cell刷新


NSIndexPath *indexPath=[NSIndexPath indexPathForRow:row inSection:section];


[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath,nil] withRowAnimation:UITableViewRowAnimationNone];


这里需要注意的是,每次单选和全选的时候,需要对底部结账栏进行数据的刷新,并且单选完整组后,需要对section上的按钮进行选中状态变化,当选中section时,同样需要对section下的row进行选中状态,如果全部商品选中,还得需要改变结账栏的状态。(这里其实不难,无非就是对model中各类字段进行改变,再刷新,同时判断选中数量的多少来进行按钮的状态变换)

2.删除单个商品

删除的话就相对容易了,直接对数据源删除对应的row,当只剩一个后,删除该数据后,记得删除该组section不然报错。删除后及时刷新底部结账栏金额显示。

3.编辑section

点击编辑按钮,修改model,展示编辑cell。编辑按钮上放了数量计数器、商品的信息、删除。

计数器:用到的是好友的一个库PPNumberButton 喜欢的大家可以去玩玩。点击后用代理方法把数量的变化跟新model。

商品信息:点击对商品的信息进行重新选择,同样修改数据源。

删除:代理出来进行model的修改。

因为这里用的是假数据,所以进行的都是对数据源的修改,正常情况下,原理都一样,可以在次基础上,如果接口成功,就对本地数据进行修改,最后提交的信息会和后台匹配一次的,如果有问题,可以自己修改一下。

有小伙伴提出demo中没有下拉刷新,其实下拉刷新不影响该demo。不过加上效果更好。

谢谢“爱在巴黎梦醒时”该小伙伴。
demo的bug注释:

因为demo中判断section的全选和编辑的按钮都是放在每个section的第一个row中的,所以删除section的第一个row后,会有全选和编辑的固定bug出来。特此声明,该bug不影响主体逻辑,如次bug影响小伙伴对逻辑思路的学习,那我后面再重新组织数据源。
demo纯代码编写的,只隔离了部分模块,因为我也是拿来练练手,所以如果有需要,后续我会把购物车模块化。如果内容有不妥和臃肿的地方,大家可以提出来,我及时学习并修改。如果大家有意见的可以@我1804094055qq.com。

项目源码Git地址https://github.com/zl645420646/-ZLShoppingCart
欢迎Star! 查看全部
淘宝.gif


ViewController: 购物车界面
整个界面就是TableView + 底部结账栏View组成

3899794-1ec96767b3e7afd5.png

以店铺为section:商店下的商品为row和店铺名称组成一个 section

定制段头的View 把section的全选按钮、点击商品、编辑的三个按钮的方法用代理的方法。

-(UIView*)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section;



3899794-6ef9165898df88f7.png

建议使用Masonry进行cell适配

cell的创建就是和我们平常的一样,把要展示的样式代码编写或者xib都可以。再把数据源填充到我们所创建好的cell中和段头上。
创建好一个View添加在TableView的下方。View上写上全选及总金额等UI。每次我们选定的物品的增减都要调用该View赋值的方法,刷新金额等字段显示。

3899794-700992bcfec1fcb0.png

Cell:物品栏

创建两种cell,一个是正常的物品显示cell,另一个cell是编辑后的cell。

正常的cell:只说下label中划线的实现

//中划线


NSDictionary *attribtDic = @{NSStrikethroughStyleAttributeName: [NSNumber numberWithInteger:NSUnderlineStyleSingle]};

NSMutableAttributedString *attribtStr = [[NSMutableAttributedString alloc]initWithString:info[@"GoodsOldPrice"] attributes:attribtDic];

// 赋值

_Goods_OldPrice.attributedText = attribtStr;



编辑后的cell:

主要由三部分组成。商品数量 + 商品种类 + 删除
3899794-6ce64a551798a1eb.png

商品数量用的是第三方PPNumberButton,点击时改变model中该商品的实际数量;点击商品种类进行重新选择(方法未实现,原理一样);点击删除进行cell的删除及数据源的删除。

注释:这里的删除 及 修改 都是要对数据源进行修改在刷新的

Model:数据源的处理及购物车内各类按钮的判断

demo中的数据源我没有放到model中去处理,其实原理都一样,我把判断各类按钮的判断字段加到数据源中去了,如果用model模型的话,自己加上相对应的字段,并设置初始值。当进行model数据源的修改时,直接进行修改。

3899794-272f9ea50f05377f.png

这是一种model处理方式,还有一种就是用JsonModel来处理,一层层的写下来,原理一样。 

购物车逻辑及实现总结

逻辑整理:当我们把有购买意向的物品加到购物车后,我们在购物车中调用接口获取购物车中的物品信息。数据源格式大概是(感觉不怎么对,但是能理解就行)

[

{@“店铺信息”:[@{物品信息},@{物品信息},@{物品信息}]},  -------》组一

{@"店铺信息":[@{物品信息}]},                                              -------》组二

{@”店铺信息“:[@{物品信息},@{物品信息}]}                        -------》组三

]

把数据源用model装起来,把数据填充到tableview中去。

1.单个商品的选择、单个店铺内所有商品的选择、结账栏下的全选                      

如果做得很简单的话,可以直接用系统的单选和全选方法。

最重要的两句 !!!!

TableDemo.editing=YES;      编辑状态

TableDemo.allowsMultipleSelectionDuringEditing=YES;   编辑的时候多选


cell.tintColor= [UIColorredColor];   选中后的颜色


选中和取消选中


-(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath    选中


-(void)tableView:(UITableView*)tableView didDeselectRowAtIndexPath:(NSIndexPath*)indexPath    取消选中



3899794-b9d2effc299da195.png

如果不用系统的话,则利用model来进行单选和全选的操作,选中和取消选中对model中的判断字段进行修改,刷新当前cell或者section。

//一个section刷新


NSIndexSet *indexSet=[[NSIndexSet alloc]initWithIndex:section];


[tableview reloadSections:indexSet


withRowAnimation:UITableViewRowAnimationAutomatic];


//一个cell刷新


NSIndexPath *indexPath=[NSIndexPath indexPathForRow:row inSection:section];


[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath,nil] withRowAnimation:UITableViewRowAnimationNone];


这里需要注意的是,每次单选和全选的时候,需要对底部结账栏进行数据的刷新,并且单选完整组后,需要对section上的按钮进行选中状态变化,当选中section时,同样需要对section下的row进行选中状态,如果全部商品选中,还得需要改变结账栏的状态。(这里其实不难,无非就是对model中各类字段进行改变,再刷新,同时判断选中数量的多少来进行按钮的状态变换)

2.删除单个商品

删除的话就相对容易了,直接对数据源删除对应的row,当只剩一个后,删除该数据后,记得删除该组section不然报错。删除后及时刷新底部结账栏金额显示。

3.编辑section

点击编辑按钮,修改model,展示编辑cell。编辑按钮上放了数量计数器、商品的信息、删除。

计数器:用到的是好友的一个库PPNumberButton 喜欢的大家可以去玩玩。点击后用代理方法把数量的变化跟新model。

商品信息:点击对商品的信息进行重新选择,同样修改数据源。

删除:代理出来进行model的修改。

因为这里用的是假数据,所以进行的都是对数据源的修改,正常情况下,原理都一样,可以在次基础上,如果接口成功,就对本地数据进行修改,最后提交的信息会和后台匹配一次的,如果有问题,可以自己修改一下。

有小伙伴提出demo中没有下拉刷新,其实下拉刷新不影响该demo。不过加上效果更好。

谢谢“爱在巴黎梦醒时”该小伙伴。


demo的bug注释:

因为demo中判断section的全选和编辑的按钮都是放在每个section的第一个row中的,所以删除section的第一个row后,会有全选和编辑的固定bug出来。特此声明,该bug不影响主体逻辑,如次bug影响小伙伴对逻辑思路的学习,那我后面再重新组织数据源。


demo纯代码编写的,只隔离了部分模块,因为我也是拿来练练手,所以如果有需要,后续我会把购物车模块化。如果内容有不妥和臃肿的地方,大家可以提出来,我及时学习并修改。如果大家有意见的可以@我1804094055qq.com。

项目源码Git地址https://github.com/zl645420646/-ZLShoppingCart
欢迎Star!
0
评论

ios V2.3.1 已发布,增加获取日志压缩文件路径接口 产品快递 iOS

产品更新 发表了文章 • 1033 次浏览 • 2017-02-20 11:48 • 来自相关话题

ios版本:V2.3.1 2016-02-17





新功能/改进:
修改HttpsOnly参数默认值,默认设置为NO(由于苹果强制ATS政策延缓, 所以SDK默认关闭httpsOnly)
[[EaseMob sharedInstance].chatManager setIsUseHttpsOnly:YES];//设置httpsonly,YES开启,NO关闭
增加获取日志压缩文件路径接口(具体上传日志方式可由开发者决定, Demo是通过邮件的形式上报日志)优化群组过多时重连卡顿问题修复离线已读回执有时丢失问题修复SDK收到特殊消息闪退问题
 
 版本历史:ios 2.x更新日志
下载地址:SDK下载 查看全部
ios版本:V2.3.1 2016-02-17
2351.jpg_wh860_.jpg


新功能/改进:
  1. 修改HttpsOnly参数默认值,默认设置为NO(由于苹果强制ATS政策延缓, 所以SDK默认关闭httpsOnly)

[[EaseMob sharedInstance].chatManager setIsUseHttpsOnly:YES];//设置httpsonly,YES开启,NO关闭

  1. 增加获取日志压缩文件路径接口(具体上传日志方式可由开发者决定, Demo是通过邮件的形式上报日志)
  2. 优化群组过多时重连卡顿问题
  3. 修复离线已读回执有时丢失问题
  4. 修复SDK收到特殊消息闪退问题

 
 版本历史:ios 2.x更新日志
下载地址:SDK下载
6
评论

基于环信写的通讯项目,实现QQ小表情教程 iOS 自定义表情

zl 发表了文章 • 1058 次浏览 • 2017-02-09 15:27 • 来自相关话题

先上效果图:










 
大家直接看下面demo就可以,搜索#pragma mark smallpngface就是所有修改的地方。
图片资源可以自己替换自己的,大家可以照猫画虎,我只是抛砖引玉。     

https://pan.baidu.com/s/1bA7eMy
  查看全部
先上效果图:

6069EDD5-304A-41BD-AE22-443F1604185A.png


568D243B-599A-43F3-A08D-E2B7C0CAC6E5.png

 
大家直接看下面demo就可以,搜索#pragma mark smallpngface就是所有修改的地方。
图片资源可以自己替换自己的,大家可以照猫画虎,我只是抛砖引玉。     

https://pan.baidu.com/s/1bA7eMy
 
3
评论

环信头像和昵称显示的详细、详细、详细教程! 头像 昵称 扩展 头像和昵称的显示 iOS 昵称 头像

xuke007008 发表了文章 • 4025 次浏览 • 2017-02-07 18:17 • 来自相关话题

写在前边:本文由江南大神的环信集成demo衍生而来!

附上大神的集成链接: http://www.imgeek.org/article/825307886 
 
通过官方的文档我们知道有两种显示头像和昵称的方式(http://docs.easemob.com/im/490integrationcases/10nickname  官方文档)

这里主要讲方式二!(通过扩展消息传递显示)
 
这里主要有三个类需要改,分别是:
EaseMessageViewController  
EaseBaseMessageCell 
chatUIhelper 

首先我们需要在发送消息的时候添加扩展字段,在EaseMessageViewController.m里。可以看到有以下方法:
#pragma mark - send message

- (void)_refreshAfterSentMessage:(EMMessage*)aMessage
{
if ([self.messsagesSource count] && [EMClient sharedClient].options.sortMessageByServerTime) {
NSString *msgId = aMessage.messageId;
EMMessage *last = self.messsagesSource.lastObject;
if ([last isKindOfClass:[EMMessage class]]) {

__block NSUInteger index = NSNotFound;
index = NSNotFound;
[self.messsagesSource enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(EMMessage *obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[EMMessage class]] && [obj.messageId isEqualToString:msgId]) {
index = idx;
*stop = YES;
}
}];
if (index != NSNotFound) {
[self.messsagesSource removeObjectAtIndex:index];
[self.messsagesSource addObject:aMessage];

//格式化消息
self.messageTimeIntervalTag = -1;
NSArray *formattedMessages = [self formatMessages:self.messsagesSource];
[self.dataArray removeAllObjects];
[self.dataArray addObjectsFromArray:formattedMessages];
[self.tableView reloadData];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.dataArray count] - 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
return;
}
}
}
[self.tableView reloadData];
}

- (void)_sendMessage:(EMMessage *)message
{
if (self.conversation.type == EMConversationTypeGroupChat){
message.chatType = EMChatTypeGroupChat;
}
else if (self.conversation.type == EMConversationTypeChatRoom){
message.chatType = EMChatTypeChatRoom;
}

[self addMessageToDataSource:message
progress:nil];

__weak typeof(self) weakself = self;
[[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:^(EMMessage *aMessage, EMError *aError) {
if (!aError) {
[weakself _refreshAfterSentMessage:aMessage];
}
else {
[weakself.tableView reloadData];
}
}];
}

- (void)sendTextMessage:(NSString *)text
{
NSDictionary *ext = nil;
if (self.conversation.type == EMConversationTypeGroupChat) {
NSArray *targets = [self _searchAtTargets:text];
if ([targets count]) {
__block BOOL atAll = NO;
[targets enumerateObjectsUsingBlock:^(NSString *target, NSUInteger idx, BOOL *stop) {
if ([target compare:kGroupMessageAtAll options:NSCaseInsensitiveSearch] == NSOrderedSame) {
atAll = YES;
*stop = YES;
}
}];
if (atAll) {
ext = @{kGroupMessageAtList: kGroupMessageAtAll};
}
else {
ext = @{kGroupMessageAtList: targets};
}
}
}
[self sendTextMessage:text withExt:ext];
}

- (void)sendTextMessage:(NSString *)text withExt:(NSDictionary*)ext
{
EMMessage *message = [EaseSDKHelper sendTextMessage:text
to:self.conversation.conversationId
messageType:[self _messageTypeFromConversationType]
messageExt:ext];
[self _sendMessage:message];
}

- (void)sendLocationMessageLatitude:(double)latitude
longitude:(double)longitude
andAddress:(NSString *)address
{
EMMessage *message = [EaseSDKHelper sendLocationMessageWithLatitude:latitude
longitude:longitude
address:address
to:self.conversation.conversationId
messageType:[self _messageTypeFromConversationType]
messageExt:nil];
[self _sendMessage:message];
}

- (void)sendImageMessageWithData:(NSData *)imageData
{
id progress = nil;
if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeImage];
}
else{
progress = self;
}

EMMessage *message = [EaseSDKHelper sendImageMessageWithImageData:imageData
to:self.conversation.conversationId
messageType:[self _messageTypeFromConversationType]
messageExt:nil];
[self _sendMessage:message];
}

- (void)sendImageMessage:(UIImage *)image
{
id progress = nil;
if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeImage];
}
else{
progress = self;
}

EMMessage *message = [EaseSDKHelper sendImageMessageWithImage:image
to:self.conversation.conversationId
messageType:[self _messageTypeFromConversationType]
messageExt:nil];
[self _sendMessage:message];
}

- (void)sendVoiceMessageWithLocalPath:(NSString *)localPath
duration:(NSInteger)duration
{
id progress = nil;
if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeVoice];
}
else{
progress = self;
}

EMMessage *message = [EaseSDKHelper sendVoiceMessageWithLocalPath:localPath
duration:duration
to:self.conversation.conversationId
messageType:[self _messageTypeFromConversationType]
messageExt:nil];
[self _sendMessage:message];
}

- (void)sendVideoMessageWithURL:(NSURL *)url
{
id progress = nil;
if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeVideo];
}
else{
progress = self;
}

EMMessage *message = [EaseSDKHelper sendVideoMessageWithURL:url
to:self.conversation.conversationId
messageType:[self _messageTypeFromConversationType]
messageExt:nil];
[self _sendMessage:message];
}
有发送各种消息的,我们要每个里边都加扩展字段么?那恐怕要累死咯!  仔细看会发现发送消息的方法最后都会走一个方法:
- (void)_sendMessage:(EMMessage *)message
{
if (self.conversation.type == EMConversationTypeGroupChat){
message.chatType = EMChatTypeGroupChat;
}
else if (self.conversation.type == EMConversationTypeChatRoom){
message.chatType = EMChatTypeChatRoom;
}

[self addMessageToDataSource:message
progress:nil];

__weak typeof(self) weakself = self;
[[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:^(EMMessage *aMessage, EMError *aError) {
if (!aError) {
[weakself _refreshAfterSentMessage:aMessage];
}
else {
[weakself.tableView reloadData];
}
}];
}
好的,就是这里了,添加扩展字段,包含用户的头像地址,昵称和环信ID。 找到保存用户信息的类UserCacheInfo,找到相应的字段,在这个方法里添加如下代码:
NSMutableDictionary *Muext = [NSMutableDictionary dictionaryWithDictionary:message.ext];
UserCacheInfo *info = [UserCacheManager currUser];
[Muext setObject:kCurrEaseUserId forKey:kChatUserId];
[Muext setObject:info.NickName forKey:kChatUserNick];
[Muext setObject:info.AvatarUrl forKey:kChatUserPic];
message.ext = Muext;

 
这样第一步就完成了!


接下来我们要在接收消息的方法里保存传过来的扩展消息里的头像、昵称和环信ID,这就用到chatUIhelper.m这个类,这个方法里:
- (void)didReceiveMessages:(NSArray *)aMessages
{
BOOL isRefreshCons = YES;
for(EMMessage *message in aMessages){
[UserCacheManager saveInfo:message.ext];// 通过消息的扩展属性传递昵称和头像时,需要调用这句代码缓存
BOOL needShowNotification = (message.chatType != EMChatTypeChat) ? [self _needShowNotification:message.conversationId] : YES;

#ifdef REDPACKET_AVALABLE
/**
* 屏蔽红包被抢消息的提示
*/
NSDictionary *dict = message.ext;
needShowNotification = (dict && [dict valueForKey:RedpacketKeyRedpacketTakenMessageSign]) ? NO : needShowNotification;
#endif

UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (needShowNotification) {
#if !TARGET_IPHONE_SIMULATOR
switch (state) {
case UIApplicationStateActive:
[self playSoundAndVibration];
break;
case UIApplicationStateInactive:
[self playSoundAndVibration];
break;
case UIApplicationStateBackground:
[self showNotificationWithMessage:message];
break;
default:
break;
}
#endif
}

if (_chatVC == nil) {
_chatVC = [self _getCurrentChatView];
}
BOOL isChatting = NO;
if (_chatVC) {
isChatting = [message.conversationId isEqualToString:_chatVC.conversation.conversationId];
}
if (_chatVC == nil || !isChatting || state == UIApplicationStateBackground) {
[self _handleReceivedAtMessage:message];

if (self.conversationListVC) {
[_conversationListVC refresh];
}

if (self.mainVC) {
NOTIFY_POST(kSetupUnreadMessageCount);
}
return;
}

if (isChatting) {
isRefreshCons = NO;
}
}

if (isRefreshCons) {
if (self.conversationListVC) {
[_conversationListVC refresh];
}

if (self.mainVC) {
NOTIFY_POST(kSetupUnreadMessageCount);
}
}
}
关键就是这句话:
[UserCacheManager saveInfo:message.ext];// 通过消息的扩展属性传递昵称和头像时,需要调用这句代码缓存!!!
 
到这里头像和昵称的问题就基本解决了!
 
 
重要的总是留在最后!!!  不看后悔哦!!!
 
上两步完成后你会惊奇的发现头像和昵称正常显示了,然而当你换个头像测试的时候,你会发现很不美妙,头像没有更换,这是什么问题呢?   这就要用到开始讲到的第一个类EaseBaseMessageCell.m,我们仔细看代码会发现它是怎么赋值的,如下:
#pragma mark - setter

- (void)setModel:(id<IMessageModel>)model
{
[super setModel:model];

if (model.avatarURLPath) {
[self.avatarView sd_setImageWithURL:[NSURL URLWithString:model.avatarURLPath] placeholderImage:model.avatarImage];
} else {
self.avatarView.image = model.avatarImage;
}
_nameLabel.text = model.nickname;

if (self.model.isSender) {
_hasRead.hidden = YES;
switch (self.model.messageStatus) {
case EMMessageStatusDelivering:
{
_statusButton.hidden = YES;
[_activity setHidden:NO];
[_activity startAnimating];
}
break;
case EMMessageStatusSuccessed:
{
_statusButton.hidden = YES;
[_activity stopAnimating];
if (self.model.isMessageRead) {
_hasRead.hidden = NO;
}
}
break;
case EMMessageStatusPending:
case EMMessageStatusFailed:
{
[_activity stopAnimating];
[_activity setHidden:YES];
_statusButton.hidden = NO;
}
break;
default:
break;
}
}
}
看到这里就明白了是头像缓存了,直接用的是缓存里的头像,我们需要更新的话直接设置一下缓存策略就可以了,代码修改如下:
把 [self.avatarView sd_setImageWithURL:[NSURL URLWithString:model.avatarURLPath] placeholderImage:model.avatarImage];
改成 [self.avatarView sd_setImageWithURL:[NSURL URLWithString:model.avatarURLPath] placeholderImage:model.avatarImage options:EMSDWebImageRefreshCached];
然后运行一下你会发现世界如此美好,大功告成!  
对各位小伙伴you有没有帮助呢? 

如有任何问题,请咨询【环信IM互帮互助群】,群号:340452063 (进群记得改名片哦!江南大神也在群里!)
本人群里的名片:上海-iOS-小码农  。 查看全部
写在前边:本文由江南大神的环信集成demo衍生而来!

附上大神的集成链接: http://www.imgeek.org/article/825307886 
 
通过官方的文档我们知道有两种显示头像和昵称的方式(http://docs.easemob.com/im/490integrationcases/10nickname  官方文档)

这里主要讲方式二!(通过扩展消息传递显示)
 
这里主要有三个类需要改,分别是:
EaseMessageViewController  
EaseBaseMessageCell 
chatUIhelper 

首先我们需要在发送消息的时候添加扩展字段,在EaseMessageViewController.m里。可以看到有以下方法:
#pragma mark - send message

- (void)_refreshAfterSentMessage:(EMMessage*)aMessage
{
if ([self.messsagesSource count] && [EMClient sharedClient].options.sortMessageByServerTime) {
NSString *msgId = aMessage.messageId;
EMMessage *last = self.messsagesSource.lastObject;
if ([last isKindOfClass:[EMMessage class]]) {

__block NSUInteger index = NSNotFound;
index = NSNotFound;
[self.messsagesSource enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(EMMessage *obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[EMMessage class]] && [obj.messageId isEqualToString:msgId]) {
index = idx;
*stop = YES;
}
}];
if (index != NSNotFound) {
[self.messsagesSource removeObjectAtIndex:index];
[self.messsagesSource addObject:aMessage];

//格式化消息
self.messageTimeIntervalTag = -1;
NSArray *formattedMessages = [self formatMessages:self.messsagesSource];
[self.dataArray removeAllObjects];
[self.dataArray addObjectsFromArray:formattedMessages];
[self.tableView reloadData];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.dataArray count] - 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
return;
}
}
}
[self.tableView reloadData];
}

- (void)_sendMessage:(EMMessage *)message
{
if (self.conversation.type == EMConversationTypeGroupChat){
message.chatType = EMChatTypeGroupChat;
}
else if (self.conversation.type == EMConversationTypeChatRoom){
message.chatType = EMChatTypeChatRoom;
}

[self addMessageToDataSource:message
progress:nil];

__weak typeof(self) weakself = self;
[[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:^(EMMessage *aMessage, EMError *aError) {
if (!aError) {
[weakself _refreshAfterSentMessage:aMessage];
}
else {
[weakself.tableView reloadData];
}
}];
}

- (void)sendTextMessage:(NSString *)text
{
NSDictionary *ext = nil;
if (self.conversation.type == EMConversationTypeGroupChat) {
NSArray *targets = [self _searchAtTargets:text];
if ([targets count]) {
__block BOOL atAll = NO;
[targets enumerateObjectsUsingBlock:^(NSString *target, NSUInteger idx, BOOL *stop) {
if ([target compare:kGroupMessageAtAll options:NSCaseInsensitiveSearch] == NSOrderedSame) {
atAll = YES;
*stop = YES;
}
}];
if (atAll) {
ext = @{kGroupMessageAtList: kGroupMessageAtAll};
}
else {
ext = @{kGroupMessageAtList: targets};
}
}
}
[self sendTextMessage:text withExt:ext];
}

- (void)sendTextMessage:(NSString *)text withExt:(NSDictionary*)ext
{
EMMessage *message = [EaseSDKHelper sendTextMessage:text
to:self.conversation.conversationId
messageType:[self _messageTypeFromConversationType]
messageExt:ext];
[self _sendMessage:message];
}

- (void)sendLocationMessageLatitude:(double)latitude
longitude:(double)longitude
andAddress:(NSString *)address
{
EMMessage *message = [EaseSDKHelper sendLocationMessageWithLatitude:latitude
longitude:longitude
address:address
to:self.conversation.conversationId
messageType:[self _messageTypeFromConversationType]
messageExt:nil];
[self _sendMessage:message];
}

- (void)sendImageMessageWithData:(NSData *)imageData
{
id progress = nil;
if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeImage];
}
else{
progress = self;
}

EMMessage *message = [EaseSDKHelper sendImageMessageWithImageData:imageData
to:self.conversation.conversationId
messageType:[self _messageTypeFromConversationType]
messageExt:nil];
[self _sendMessage:message];
}

- (void)sendImageMessage:(UIImage *)image
{
id progress = nil;
if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeImage];
}
else{
progress = self;
}

EMMessage *message = [EaseSDKHelper sendImageMessageWithImage:image
to:self.conversation.conversationId
messageType:[self _messageTypeFromConversationType]
messageExt:nil];
[self _sendMessage:message];
}

- (void)sendVoiceMessageWithLocalPath:(NSString *)localPath
duration:(NSInteger)duration
{
id progress = nil;
if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeVoice];
}
else{
progress = self;
}

EMMessage *message = [EaseSDKHelper sendVoiceMessageWithLocalPath:localPath
duration:duration
to:self.conversation.conversationId
messageType:[self _messageTypeFromConversationType]
messageExt:nil];
[self _sendMessage:message];
}

- (void)sendVideoMessageWithURL:(NSURL *)url
{
id progress = nil;
if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeVideo];
}
else{
progress = self;
}

EMMessage *message = [EaseSDKHelper sendVideoMessageWithURL:url
to:self.conversation.conversationId
messageType:[self _messageTypeFromConversationType]
messageExt:nil];
[self _sendMessage:message];
}

有发送各种消息的,我们要每个里边都加扩展字段么?那恐怕要累死咯!  仔细看会发现发送消息的方法最后都会走一个方法:
- (void)_sendMessage:(EMMessage *)message
{
if (self.conversation.type == EMConversationTypeGroupChat){
message.chatType = EMChatTypeGroupChat;
}
else if (self.conversation.type == EMConversationTypeChatRoom){
message.chatType = EMChatTypeChatRoom;
}

[self addMessageToDataSource:message
progress:nil];

__weak typeof(self) weakself = self;
[[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:^(EMMessage *aMessage, EMError *aError) {
if (!aError) {
[weakself _refreshAfterSentMessage:aMessage];
}
else {
[weakself.tableView reloadData];
}
}];
}

好的,就是这里了,添加扩展字段,包含用户的头像地址,昵称和环信ID。 找到保存用户信息的类UserCacheInfo,找到相应的字段,在这个方法里添加如下代码:
NSMutableDictionary *Muext = [NSMutableDictionary dictionaryWithDictionary:message.ext];
UserCacheInfo *info = [UserCacheManager currUser];
[Muext setObject:kCurrEaseUserId forKey:kChatUserId];
[Muext setObject:info.NickName forKey:kChatUserNick];
[Muext setObject:info.AvatarUrl forKey:kChatUserPic];
message.ext = Muext;

 
这样第一步就完成了!


接下来我们要在接收消息的方法里保存传过来的扩展消息里的头像、昵称和环信ID,这就用到chatUIhelper.m这个类,这个方法里:
- (void)didReceiveMessages:(NSArray *)aMessages
{
BOOL isRefreshCons = YES;
for(EMMessage *message in aMessages){
[UserCacheManager saveInfo:message.ext];// 通过消息的扩展属性传递昵称和头像时,需要调用这句代码缓存
BOOL needShowNotification = (message.chatType != EMChatTypeChat) ? [self _needShowNotification:message.conversationId] : YES;

#ifdef REDPACKET_AVALABLE
/**
* 屏蔽红包被抢消息的提示
*/
NSDictionary *dict = message.ext;
needShowNotification = (dict && [dict valueForKey:RedpacketKeyRedpacketTakenMessageSign]) ? NO : needShowNotification;
#endif

UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (needShowNotification) {
#if !TARGET_IPHONE_SIMULATOR
switch (state) {
case UIApplicationStateActive:
[self playSoundAndVibration];
break;
case UIApplicationStateInactive:
[self playSoundAndVibration];
break;
case UIApplicationStateBackground:
[self showNotificationWithMessage:message];
break;
default:
break;
}
#endif
}

if (_chatVC == nil) {
_chatVC = [self _getCurrentChatView];
}
BOOL isChatting = NO;
if (_chatVC) {
isChatting = [message.conversationId isEqualToString:_chatVC.conversation.conversationId];
}
if (_chatVC == nil || !isChatting || state == UIApplicationStateBackground) {
[self _handleReceivedAtMessage:message];

if (self.conversationListVC) {
[_conversationListVC refresh];
}

if (self.mainVC) {
NOTIFY_POST(kSetupUnreadMessageCount);
}
return;
}

if (isChatting) {
isRefreshCons = NO;
}
}

if (isRefreshCons) {
if (self.conversationListVC) {
[_conversationListVC refresh];
}

if (self.mainVC) {
NOTIFY_POST(kSetupUnreadMessageCount);
}
}
}

关键就是这句话:
[UserCacheManager saveInfo:message.ext];// 通过消息的扩展属性传递昵称和头像时,需要调用这句代码缓存!!!
 
到这里头像和昵称的问题就基本解决了!
 
 
  • 重要的总是留在最后!!!  不看后悔哦!!!

 
上两步完成后你会惊奇的发现头像和昵称正常显示了,然而当你换个头像测试的时候,你会发现很不美妙,头像没有更换,这是什么问题呢?   这就要用到开始讲到的第一个类EaseBaseMessageCell.m,我们仔细看代码会发现它是怎么赋值的,如下:
#pragma mark - setter

- (void)setModel:(id<IMessageModel>)model
{
[super setModel:model];

if (model.avatarURLPath) {
[self.avatarView sd_setImageWithURL:[NSURL URLWithString:model.avatarURLPath] placeholderImage:model.avatarImage];
} else {
self.avatarView.image = model.avatarImage;
}
_nameLabel.text = model.nickname;

if (self.model.isSender) {
_hasRead.hidden = YES;
switch (self.model.messageStatus) {
case EMMessageStatusDelivering:
{
_statusButton.hidden = YES;
[_activity setHidden:NO];
[_activity startAnimating];
}
break;
case EMMessageStatusSuccessed:
{
_statusButton.hidden = YES;
[_activity stopAnimating];
if (self.model.isMessageRead) {
_hasRead.hidden = NO;
}
}
break;
case EMMessageStatusPending:
case EMMessageStatusFailed:
{
[_activity stopAnimating];
[_activity setHidden:YES];
_statusButton.hidden = NO;
}
break;
default:
break;
}
}
}

看到这里就明白了是头像缓存了,直接用的是缓存里的头像,我们需要更新的话直接设置一下缓存策略就可以了,代码修改如下:
[self.avatarView sd_setImageWithURL:[NSURL URLWithString:model.avatarURLPath] placeholderImage:model.avatarImage];
改成 [self.avatarView sd_setImageWithURL:[NSURL URLWithString:model.avatarURLPath] placeholderImage:model.avatarImage options:EMSDWebImageRefreshCached];

然后运行一下你会发现世界如此美好,大功告成!  
对各位小伙伴you有没有帮助呢? 

如有任何问题,请咨询【环信IM互帮互助群】,群号:340452063 (进群记得改名片哦!江南大神也在群里!)
本人群里的名片:上海-iOS-小码农  。
1
评论

环信及时通信iOS SDK的一些看法和建议 环信 ios iOS

xiaohao01 发表了文章 • 914 次浏览 • 2017-01-16 11:46 • 来自相关话题

先说下环境:我使用的是HyphenateLite,3.2.3版本
 1,关于bitcode
终于看到3.2.3版本有说:sdk支持bitcode拉,赶紧下载组装。编译时提示:
libopencore-amrnb.a(wrapper.o)' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
看来官方给出的sdk支持bitcode还真是仅限于“sdk”了,EaseUI里的libopencore-amrnb不是环信出品的,就不管了??可这对开发者来说有什么用呢?我们需要sdk提供的全部组件都支持bicode才行呢!!!
另外:跟iOS SDK客服沟通了下,似乎没有搞明白开启bitcode时什么意思。
强调一遍:必须在TARGET中设置bitcode为YES,然后真机运行,真机运行!!!!
麻烦官方再仔细检查一遍给我们开发者一个真正能用的bitcode版本!!!
 
2,关于文档
这次3.2.3说使用了动态framework,嗯,对官方的这种与时俱进赞一个。但你们能仔细看看给的文档吗:
注: 由于 iOS 编译的特殊性,为了方便开发者使用,我们将 i386 x86_64 armv7 arm64 几个平台都合并到了一起,所以使用动态库上传appstore时需要将i386 x86_64两个平台删除后,才能正常提交
然后呢?如何操作?能再简洁一点吗?
熟悉开发的肯定都知道用lipo操作,但是刚入门的是还需要查询的呀。能直接说下怎么操作岂不是更好!
 
3,一些建议
1,做SDK不像平常开发个app,自己的一亩三分地想怎么搞就怎么搞,sdk是拿出来给大家用的,对象都是开发者,需要开发sdk的同学本身就非常精通。一个建议,对于暴漏的.h文件最好都引用:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>//如何有UI
 
2,并且文档什么的要尽量细化。看到官方推出了不少视频的开发文档,这个虽然很好,降低了门槛,可是sdk总是在升级的在变动的,视频的内容往往发出来不久就落后于实际的代码了。建议多些文字文档,重要步骤尽量详细,文字能快速传达信息,没人有希望集成个sdk都要花费很多时间。
 
以上是我个人的浅见,不是纯粹是发牢骚,大家都是开发者,都希望作品精益求精,希望共同进步!
 
匆促中难免有错字,望理解!谢谢!
  查看全部
先说下环境:我使用的是HyphenateLite,3.2.3版本
 1,关于bitcode
终于看到3.2.3版本有说:sdk支持bitcode拉,赶紧下载组装。编译时提示:
libopencore-amrnb.a(wrapper.o)' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
看来官方给出的sdk支持bitcode还真是仅限于“sdk”了,EaseUI里的libopencore-amrnb不是环信出品的,就不管了??可这对开发者来说有什么用呢?我们需要sdk提供的全部组件都支持bicode才行呢!!!
另外:跟iOS SDK客服沟通了下,似乎没有搞明白开启bitcode时什么意思。
强调一遍:必须在TARGET中设置bitcode为YES,然后真机运行,真机运行!!!!
麻烦官方再仔细检查一遍给我们开发者一个真正能用的bitcode版本!!!
 
2,关于文档
这次3.2.3说使用了动态framework,嗯,对官方的这种与时俱进赞一个。但你们能仔细看看给的文档吗:

注: 由于 iOS 编译的特殊性,为了方便开发者使用,我们将 i386 x86_64 armv7 arm64 几个平台都合并到了一起,所以使用动态库上传appstore时需要将i386 x86_64两个平台删除后,才能正常提交


然后呢?如何操作?能再简洁一点吗?
熟悉开发的肯定都知道用lipo操作,但是刚入门的是还需要查询的呀。能直接说下怎么操作岂不是更好!
 
3,一些建议
1,做SDK不像平常开发个app,自己的一亩三分地想怎么搞就怎么搞,sdk是拿出来给大家用的,对象都是开发者,需要开发sdk的同学本身就非常精通。一个建议,对于暴漏的.h文件最好都引用:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>//如何有UI
 
2,并且文档什么的要尽量细化。看到官方推出了不少视频的开发文档,这个虽然很好,降低了门槛,可是sdk总是在升级的在变动的,视频的内容往往发出来不久就落后于实际的代码了。建议多些文字文档,重要步骤尽量详细,文字能快速传达信息,没人有希望集成个sdk都要花费很多时间。
 
以上是我个人的浅见,不是纯粹是发牢骚,大家都是开发者,都希望作品精益求精,希望共同进步!
 
匆促中难免有错字,望理解!谢谢!
 
2
评论

环信动态库sdk上架问题解决方案 iOS 动态库

beyond 发表了文章 • 2807 次浏览 • 2017-01-10 17:28 • 来自相关话题

环信发布了动态库sdk:




但是也会有一些问题,这里讲下关于这个上架的问题。

1.先把Hyphenate.framework放到桌面上;

2.终端位置cd到桌面;

3.运行:lipo Hyphenate.framework/Hyphenate -thin armv7 -output Hyphenate_armv7  

4.运行后没有输出提示,直接运行下一个命令:lipo Hyphenate.framework/Hyphenate -thin arm64 -output Hyphenate_arm64                                                                                                                                                                

5.运行后一样没有输出提示,直接运行下一个命令:lipo -create Hyphenate_armv7 Hyphenate_arm64 -output Hyphenate                                                                                                                                                                            

6.运行后一样没有输出提示,直接运行最后一个命令:mv Hyphenate Hyphenate.framework/




得到的Hyphenate.framework就是最后的结果,拖进工程,编译打包上架。
 
作者:环信ios工程师张磊 查看全部
环信发布了动态库sdk:
2639170-be3d6bfe875d544c.png

但是也会有一些问题,这里讲下关于这个上架的问题。

1.先把Hyphenate.framework放到桌面上;

2.终端位置cd到桌面;

3.运行:lipo Hyphenate.framework/Hyphenate -thin armv7 -output Hyphenate_armv7  

4.运行后没有输出提示,直接运行下一个命令:lipo Hyphenate.framework/Hyphenate -thin arm64 -output Hyphenate_arm64                                                                                                                                                                

5.运行后一样没有输出提示,直接运行下一个命令:lipo -create Hyphenate_armv7 Hyphenate_arm64 -output Hyphenate                                                                                                                                                                            

6.运行后一样没有输出提示,直接运行最后一个命令:mv Hyphenate Hyphenate.framework/
2639170-6aa66fdee8102250.png

得到的Hyphenate.framework就是最后的结果,拖进工程,编译打包上架。
 
作者:环信ios工程师张磊
0
评论

Android ios V3.2.3 SDK 已发布,SDK十余项更新,更加简洁易用,新增广告红包 产品快递 Android iOS

产品更新 发表了文章 • 1033 次浏览 • 2016-12-30 11:51 • 来自相关话题

Android​ V3.2.3 2016-12-29
新功能/优化:
sdk提供aar及gradle方式集成,具体方法查看gradle方式导入aar增加离线推送设置的相关接口,具体方法可查看EMPushManager API文档为了使sdk更简洁易用,修改以及过时了一些api,具体修改查看3.2.3api修改,另外过时的api后续3-5个版本会进行删除优化loadAllConversationsFromDB()方法,从联表查询改为从两个表分别查询,解决在个别乐视手机上执行很慢的问题优化登录模块,减少登录失败的概率鉴于市面上的手机基本都是armeabi-v7a及以上的架构,从这版本开始不再提供普通的armeabi架构的so,减少打包时app的体积

红包相关:
新增:
小额随机红包增加广告红包(需要使用请单独联系商务)商户后台增加广告红包配置、统计功能商户后台增加修改密码功能

优化:
绑卡后的用户验证四要素改为验证二要素发红包等页面增加点击空白区域收回键盘的功能群成员列表索引增加常用姓氏以及汉字的支持

修复bug:
红包详情页领取人列表展示不全华为P8手机密码框无法获取焦点部分银行卡号输入正确,提示银行卡号不正确红包祝福语有换行符显示不正确修复Emoji表情显示乱码修复商户自主配置红包最低限额错误修复零钱明细显示顺序错误问题
 
iOS​ V3.2.3 2016-12-29
新功能/优化:
新增:实时1v1音视频,设置了对方不在线发送离线推送的前提下,当对方不在线时返回回调,以便于用户自定义离线消息推送更新:SDK支持bitcode更新:SDK使用动态库为了使SDK更简洁易用,过时的API会在后续3~5个版本进行删除

红包相关:
新增:
小额随机红包商户后台增加修改密码功能

优化:
绑卡后的用户验证四要素改为验证二要素iOS和Android两端UI展示一致性支付流程的优化SDK注册流程去掉XIB集成过程的参数检查风险策略

修复:
SDKToken注册失败的问题发红包缺少参数的问题修复Emoji表情显示乱码修复支付密码可能误报出错修复商户自主配置红包最低限额错误修复零钱明细显示顺序错误问题修改抢红包流程为依赖后端数据修复支行信息返回为空时的文案
 
 版本历史:Android SDK更新日志  ios SDK更新日志
下载地址:SDK下载 查看全部
7750.jpg_wh860_.jpg

Android​ V3.2.3 2016-12-29
新功能/优化:
  • sdk提供aar及gradle方式集成,具体方法查看gradle方式导入aar
  • 增加离线推送设置的相关接口,具体方法可查看EMPushManager API文档
  • 为了使sdk更简洁易用,修改以及过时了一些api,具体修改查看3.2.3api修改,另外过时的api后续3-5个版本会进行删除
  • 优化loadAllConversationsFromDB()方法,从联表查询改为从两个表分别查询,解决在个别乐视手机上执行很慢的问题
  • 优化登录模块,减少登录失败的概率
  • 鉴于市面上的手机基本都是armeabi-v7a及以上的架构,从这版本开始不再提供普通的armeabi架构的so,减少打包时app的体积


红包相关:
新增:

  • 小额随机红包
  • 增加广告红包(需要使用请单独联系商务)
  • 商户后台增加广告红包配置、统计功能
  • 商户后台增加修改密码功能


优化:
  • 绑卡后的用户验证四要素改为验证二要素
  • 发红包等页面增加点击空白区域收回键盘的功能
  • 群成员列表索引增加常用姓氏以及汉字的支持


修复bug:
  • 红包详情页领取人列表展示不全
  • 华为P8手机密码框无法获取焦点
  • 部分银行卡号输入正确,提示银行卡号不正确
  • 红包祝福语有换行符显示不正确
  • 修复Emoji表情显示乱码
  • 修复商户自主配置红包最低限额错误
  • 修复零钱明细显示顺序错误问题

 
iOS​ V3.2.3 2016-12-29
新功能/优化:
  • 新增:实时1v1音视频,设置了对方不在线发送离线推送的前提下,当对方不在线时返回回调,以便于用户自定义离线消息推送
  • 更新:SDK支持bitcode
  • 更新:SDK使用动态库
  • 为了使SDK更简洁易用,过时的API会在后续3~5个版本进行删除


红包相关:
新增:

  • 小额随机红包
  • 商户后台增加修改密码功能


优化:
  • 绑卡后的用户验证四要素改为验证二要素
  • iOS和Android两端UI展示一致性
  • 支付流程的优化
  • SDK注册流程
  • 去掉XIB
  • 集成过程的参数检查
  • 风险策略


修复:
  • SDKToken注册失败的问题
  • 发红包缺少参数的问题
  • 修复Emoji表情显示乱码
  • 修复支付密码可能误报出错
  • 修复商户自主配置红包最低限额错误
  • 修复零钱明细显示顺序错误问题
  • 修改抢红包流程为依赖后端数据
  • 修复支行信息返回为空时的文案

 
 版本历史:Android SDK更新日志  ios SDK更新日志
下载地址:SDK下载
0
评论

ios V2.3.0 SDK 已发布,增加HttpsOnly参数,允许用户配置 iOS 产品快递

产品更新 发表了文章 • 587 次浏览 • 2016-12-28 14:46 • 来自相关话题

ios V2.3.0 2016-12-28




新功能/改进:
修复2.2.9升级覆盖2.1.5至2.2.3版本,可能无法登录的bug

增加HttpsOnly参数,允许用户配置,默认设置为YES

SDK支持bitcode
版本历史:更新日志
下载地址:SDK下载 查看全部
ios V2.3.0 2016-12-28
1494.jpg_wh860_.jpg

新功能/改进:

修复2.2.9升级覆盖2.1.5至2.2.3版本,可能无法登录的bug

增加HttpsOnly参数,允许用户配置,默认设置为YES

SDK支持bitcode


版本历史:更新日志
下载地址:SDK下载
0
评论

IOS 2.2.9设置https only遇到无法登录的解决方案 ios_2.X iOS 环信集成指南 https

beyond 发表了文章 • 1119 次浏览 • 2016-12-28 14:34 • 来自相关话题

分享一个自己遇到的在环信IOS2.X中设置https登录不上的解决方案。
 
SDK的老版本(版本范围为SDK2.1.5-2.2.3)中存在默认不使用https的设置。部分使用了范围内SDK版本的用户在升级到最新的SDK2.2.9时,设置https only的选项后会出现用户无法正常登录的问题。

这个问题可以升级到SDK2.2.9及以上版本,在SDK初始化时添加otherConfig:@{kSDKConfigUseHttps:@YES}的设置,具体的代码实现如下:[[EaseMob sharedInstance] registerSDKWithAppKey:@"easemob-demo#chatdemoui" apnsCertName:@"chatdemoui" otherConfig:@{kSDKConfigUseHttps:@YES}];
上记代码仅未示例,具体在使用时需要将appkey等信息替换为自己的对应信息就可以了。
 
集成过程中遇到问题欢迎在IMGeek社区发帖咨询。 查看全部
分享一个自己遇到的在环信IOS2.X中设置https登录不上的解决方案。
 
SDK的老版本(版本范围为SDK2.1.5-2.2.3)中存在默认不使用https的设置。部分使用了范围内SDK版本的用户在升级到最新的SDK2.2.9时,设置https only的选项后会出现用户无法正常登录的问题。

这个问题可以升级到SDK2.2.9及以上版本,在SDK初始化时添加otherConfig:@{kSDKConfigUseHttps:@YES}的设置,具体的代码实现如下:
[[EaseMob sharedInstance] registerSDKWithAppKey:@"easemob-demo#chatdemoui" apnsCertName:@"chatdemoui" otherConfig:@{kSDKConfigUseHttps:@YES}];

上记代码仅未示例,具体在使用时需要将appkey等信息替换为自己的对应信息就可以了。
 
集成过程中遇到问题欢迎在IMGeek社区发帖咨询。
1
评论

ios V3.2.2 SDK 已发布,增加是否删除会话选项 ios_3.x iOS 产品快递

产品更新 发表了文章 • 1006 次浏览 • 2016-12-08 19:10 • 来自相关话题

ios ​V3.2.2 2016-12-08





 
新功能/优化:
 

SDK满足apple ATS的要求

删除好友逻辑的修改(增加是否删除会话选项)

修复呼叫时对方不在线,不能正确显示通话结束原因的问题

版本历史:更新日志 
下载地址:SDK下载 查看全部
ios ​V3.2.2 2016-12-08

2597.jpg_wh860_.jpg

 
新功能/优化:
 


SDK满足apple ATS的要求

删除好友逻辑的修改(增加是否删除会话选项)

修复呼叫时对方不在线,不能正确显示通话结束原因的问题


版本历史:更新日志 
下载地址:SDK下载
0
评论

ios V2.2.9 SDK 已发布,SDK满足apple ATS的要求 ios_2.X iOS 产品快递

产品更新 发表了文章 • 1039 次浏览 • 2016-12-08 18:54 • 来自相关话题

ios 版本 V3.2.2 2016-12-08





 
新功能/优化:

SDK满足apple ATS的要求

删除好友逻辑的修改(增加是否删除会话选项)

修复呼叫时对方不在线,不能正确显示通话结束原因的问题

版本历史:更新日志 
下载地址:SDK下载 查看全部
ios 版本 V3.2.2 2016-12-08
7282.jpg_wh860_.jpg


 
新功能/优化:


SDK满足apple ATS的要求

删除好友逻辑的修改(增加是否删除会话选项)

修复呼叫时对方不在线,不能正确显示通话结束原因的问题


版本历史:更新日志 
下载地址:SDK下载
0
评论

ios V3.2.1 SDK 已发布,聊天室列表支持分页获取 iOS 产品快递

产品更新 发表了文章 • 835 次浏览 • 2016-12-07 11:33 • 来自相关话题

ios V3.2.1 2016-11-12
 





新功能/优化:
 
聊天室列表支持分页获取

EMOption中usingHttps默认为YES

bug fix:
修复Lite版本SDK编译warning的问题
 
版本历史:更新日志  
下载地址:SDK下载 查看全部
ios V3.2.1 2016-11-12
 
3099.jpg_wh860_.jpg


新功能/优化:
 

聊天室列表支持分页获取

EMOption中usingHttps默认为YES



bug fix:

修复Lite版本SDK编译warning的问题


 
版本历史:更新日志  
下载地址:SDK下载
0
评论

快速找到当前页面的viewcontroller,有助于看别人代码 iOS

奋斗的蜗牛 发表了文章 • 586 次浏览 • 2016-10-28 17:52 • 来自相关话题

下面我将上传一个微信demo试试看了下效果,只需要往工程里面添加创建的视图控制器,就可以快速找到当前页面的viewcontroler




 
下面我将上传一个微信demo试试看了下效果,只需要往工程里面添加创建的视图控制器,就可以快速找到当前页面的viewcontroler
屏幕快照_2016-10-28_下午5.47_.49_.png