iOS开发利用系统推送Notifaction和轮询实现简单聊天系统

话不多说,先看一下做好的聊天软件界面:




首先在StoryBoard里拖了一个UItableView和一个view用来输入文字或者语音,右边的按钮用来切换文字和语音:


聊天里有三种id:
orderID :聊天id
messageID :每条消息的ID
sessionID :每个订单的会话ID,如果为空通过orderID请求。


然后在viewDidLoad里做一些界面上的操作和一些初始化的操作:
1.设置一下tableview的headView


2.初始化录音、用户头像、获取订单详情
[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <strong>   </strong><span style="font-size:14px;"//初始换录音  
  2.     [self initRecord];  
  3.     //获取用户头像  
  4.     [self getHeadImg];  
  5.     //获取订单详情  
  6.     [self getOrderDetailInfo];</span>  
 
3.在viewWillAppear里:
a.注册了一些键盘和聊天消息的通知
b.启动了一个20秒的NSTimer轮询获取聊天消息
c.self.chatArray读取数据库的聊天消息,如果数组为空返回,如果不为空刷新tableview
d .如果是从评价页面过来,刷新订单状态

4.看一下轮询消息的代码:

[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:14px;">#pragma mark - 轮询消息  
  2. - (void)runLoopMessage {  
  3.     SpeakType speaker = [YCChatInfo getSpeakerPassengerBy:self.orderInfo.bigType];  
  4.     [[YCReceiveMessageCenter defaultMessageCenter] getMessageListBySpeaker:speaker  
  5.                                      isRemote:NO  
  6.                                     andPushId:nil];  
  7. }</span>  
第一句代码是获取会话类型,这里定义了一个枚举值,主要有以下几个角色:
[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. //会话类型  
  2. enum SpeakType {  
  3.     //Business  
  4.     YCDriverType     = 11,  
  5.     YCPassenger      = 12,  
  6.     YCSystem         = 13,      //v5.2.2 增加系统角色  
  7.     YCLoctionAutoReply  = 14,  //v5.2.2添加本地自动回复  
  8.     YCDriverAutoReply   = 15,  //v5.2.2添加司机自动回复 司机角色  
  9.     YCLoctionUpdateVersionReply = 16//v5.2.2 未知消息类型回复 【提示不支持的消息类型。请升级】  
  10. };  
  11. typedef NSInteger SpeakType;  
然后根据会话类型去请求聊天list接口,然后请求成功后对数据进行处理:
[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. //isRemote 点击推送栏消息  
  2. if (response && [response[@"ret_code"] integerValue] == 200) {  
  3.     NSArray *array = response[@"result"];  
  4.     id topVC = [[YCAppDelegate sharedDelegate] topViewController];  
  5.     __block NSString *orderID = nil;  
  6.     __block NSString *dType = nil;  
  7.     [array enumerateObjectsUsingBlock:^(NSDictionary *result, NSUInteger idx, BOOLBOOL *stop) {  
  8.         NSString *type = [self controlMessageDispatch:result];  
  9.         dType = type;  
  10. lt;span style="white-space:pre">     </span>//此处省略五百字  
  11.     }  
  12. else {  
  13.     DLog(@"轮询数据失败 response = %@, error = %@", response, error);  
  14. }  

如果code == 200的时候证明请求成功,然后用数组取出所有的聊天消息,然后用enumerateObjectsUsingBlock方法便利数组里每个元素,每个元素即一条聊天消息。然后通过controlMessageDispatch来获取消息的类型(dType):
[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. typedef NS_ENUM(NSInteger, ClassType) {  
  2.    OrderClass = 1,  
  3.    ChatClass = 2,  
  4.    UserClass = 3,  
  5. };  
第一个是订单类型消息,第二个是聊天类型消息,第三个是账户消息。
如果是订单消息,根据type去判断是什么状态,然后去发不同的Notification。如果是聊天类型把result传入:
[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. - (void)receiveChatMessage:(id)object  
方法,然后有一个消息状态字段kChatRepeatState,如果kChatRepeatState == 20,表示已读消息,直接返回。如果不是,用content初始化YCChatInfo:
[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. NSDictionary *content = dic[@"content"];  
  2. YCChatInfo *item;  
  3. item = [[YCChatInfo alloc] initWithDictionary:content];  
然后去判断ChatType,有以下几种:
[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. //聊天 类型  
  2. enum ChatType {  
  3.     ChatText       = 1,  
  4.     ChatImage      = 2,  
  5.     ChatAudio      = 3,  
  6.     ChatPOI        = 4,  
  7.     ChatMix        = 5//混合内容  
  8.     ChatCard       = 6,//v5.2.2卡片消息  
  9.     ChatUpdateHint     = 701 //v5.2.2不支持类型升级提示  
  10.       
  11. };  
  12. typedef NSInteger ChatType;  

如果是语音消息,需要异步先去请求下载语音消息,下载完后先显示到界面上同事置为未读消息然后再储存到数据库里,然后回到主线程发NotifactionName:kChatMessageNotification通知聊天界面接收到聊天信息,展示到界面后然后存到数据库中:
[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. [[NSNotificationCenter defaultCenter] postNotificationName:kChatMessageNotification  
  2.                                                     object:nil  
  3.                                                   userInfo:@{@"chatInfo" : chatInfo}];  
  4. [self.chatStore insertChat:chatInfo];  
这里上次有个bug,聊天消息去重之后一直收不到语音消息,就是因为我先把新聊天消息先插入数据,然后再去发通知,导致往界面上显示聊天消息是总是显示不上去。

如果是文字消息,把state置为MessageRead已读,然后发Notifaction通知聊天页面,然后存到数据库,如果是其他消息类型,把content改为“不支持的消息类型|您的当前版本过低,点击升级客户端”,然后在发出通知,存到数据库里。

如果是第三种账户消息,显示小红点,然后发Notifaction通知viewcontroller消息中心有新消息。
接着获取到dType后
[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. //首先判断是否是推送消息, 且判断该条点击的推送id进入的  
  2.                 if (isRemote && [pushId isEqualToString:result[@"id"]]) {  
  3.                     if (!orderID && ([type isEqualToString:@"new_chat"] ||  
  4.                                      [type isEqualToString:@"DRIVER_ARRIVE"] ||  
  5.                                      [type isEqualToString:@"RECEPTION_DRIVER"]||  
  6.                                      [type isEqualToString:@"SERVICE_DONE"])) {  
  7.                         if ([type isEqualToString:@"new_chat"]) {  
  8.                             orderID = result[@"content"][@"topic"];  
  9.                         } else if (![topVC isKindOfClass:[YCSelectDriverViewController class]] &&  
  10.                                    ([type isEqualToString:@"DRIVER_ARRIVE"] ||  
  11.                                     [type isEqualToString:@"RECEPTION_DRIVER"] ||  
  12.                                     [type isEqualToString:@"SERVICE_DONE"])) {  
  13.                                        orderID = result[@"content"][@"order_id"];  
  14.                         }  
  15.                     }  
  16.                 }  
  17.             }];  
  18.             if (orderID) {  
  19.                 if ([DefaultValueForKey(kShowWelcome) boolValue]) {  
  20.                     if (![topVC isKindOfClass:[YCWelcomeVC class]]) {  
  21.                         [[NSNotificationCenter defaultCenter] postNotificationName:kRemotePushVC  
  22.                                                                             object:nil  
  23.                                                                           userInfo:@{@"orderID" : orderID,  
  24.                                                                                      @"type":dType}];  
  25.                     }  
  26.                 }  
  27.             }  

首先判断是否是推送消息, 且判断该条点击的推送id进入的,如果orderID不存在且如果type是新聊天消息或司机已到达或者司机接单或者服务结束就进入if判断里,然后在判断type是不是聊天,如果是聊天orderID是content里的topic字段,如果不是新聊天且当前最顶层ViewController不是YCSelectDriverViewController类且type是司机已到达或者司机接单或者服务结束就进入if判断里,orderID是content里的order_id字段。
如果orderID存在的情况下先判断欢迎页面是不是显示过,然后再判断当前最顶层ViewController不是欢迎页类,然后就发出Notifaction,然后通知view跳转到指定的ViewController页面。

当接收到聊天消息,经过一系列数据处理后,发出通知,然后YCChatViewController里会接到通知调用- (void)receiveMessage:(NSNotification *)notification方法:
[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. - (void)receiveMessage:(NSNotification *)notification {  
  2.     YCChatInfo *chatInfo  = notification.userInfo[@"chatInfo"];  
  3.     //如果此时来的消息不输入当前会话 页面不进行操作  
  4.     NSString *string1 = [[NSString alloc] initWithFormat:@"%@", chatInfo.orderID];  
  5.     NSString *string2 = [[NSString alloc] initWithFormat:@"%@"self.orderInfo.serverOrderId];  
  6.     if (![string1 isEqualToString:string2]) {  
  7.         return ;  
  8.     }  
  9.     NSInteger messageID = chatInfo.messageID;  
  10.     YCChatStore *chatStore = [[YCChatStore alloc]init];  
  11.     BOOL isNotRepeat = [chatStore selectDBwithMessageID:messageID];  
  12.     if (!isNotRepeat) {  
  13.         return;  
  14.     }  
  15.     [self insertRowToTableViewByIndexPath:chatInfo isSend:NO];  
  16. }  
第一句话是获取通知里传的聊天消息内容,然后拿chatinfo里的orderID和通过获取订单详情的接口里获得的orderID做比较,如果orderID不一致,直接return。如果一直就去YCChatStore里的selectDBwithMessageID方法里查询数据库是否有相同的messageID存在,如果存在直接return,如果不存在就插入界面
[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. - (NSIndexPath *)insertRowToTableViewByIndexPath:(YCChatInfo *)chatInfo isSend:(BOOL)isSend {  
  2.     NSIndexPath *indexPath;  
  3.     [self.chatArray addObject:chatInfo];  
  4.       
  5.     indexPath = [NSIndexPath indexPathForRow:[self.chatArray count] - 1  
  6.                                    inSection:0];  
  7.     void(^ScrollBlock)() = ^{  
  8.         DLog(@"%d",indexPath.row);  
  9.         [self.tableView scrollToRowAtIndexPath:indexPath  
  10.                               atScrollPosition:UITableViewScrollPositionBottom  
  11.                                       animated:YES];  
  12.     };  
  13.     [self.tableView insertRowsAtIndexPaths:@[indexPath]  
  14.                           withRowAnimation:YCTableViewRowAnimationFromBottom  
  15.                                 completion:^{  
  16.                                     if (isSend) {  
  17.                                         ScrollBlock();  
  18.                                     }  
  19.     }];  
  20.       
  21.     if (!isSend) {  
  22.         ScrollBlock();  
  23.     }  
  24.       
  25.     return indexPath;  
  26. }  

- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated这个方法是把第几个indexpath滑动到tableview的最底部,
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths
              withRowAnimation:(YCTableViewRowAnimation)animation
                    completion:(void(^)(void))animationCompletion
这是插入时候的一个动画效果。

简单介绍一下聊天系统里边所有model、view、controller相关类的名字和功能:

MODEL:

YCChatInfo: 主要定义了聊天接口里所有用到字段的定义,聊天相关的枚举信息和聊天消息的一些BOOL判断。
YCChatStore :主要是对聊天数据进行数据库的建表、插入、删除、查询、去重、更新等操作。
YCChatRequest :封装了聊天相关的网络请求。
YCChatRecord:主要是录音相关的一些封装,包括开始录音、结束录音、获取录音时长、音频格式转化、以及通过SessionID获取音频路径。
YCReceiveMessageCenter:相当于NotifactionCenter,是聊天消息处理的一个消息中心,所有接受的聊天消息和发送的聊天消息都会经过MessageCenter处理。

Views:

YCChatTableView :继承tableview的类,里边重写了tableview的插入动画,就是每次来新消息时候的插入动画。
YCChatBaseCell  : 聊天里所有的cell都继承自此cell(文字、语音,未来还可能包括图片、地理位置等等)里边主要定义了聊天时间的Label、背景图片、头像、司机名、发送时候的loading
YCChatAudioCell :语音聊天cell,包括播放按钮、小红点提示、录音时间label
YCChatImageCell :系统中暂时没有用到,可能是为以后聊天可以发图片做准备
YCChatTextCell :  发送文字聊天cell
YCHeadView :头像cell
YCRecordView : 聊天界面中按住说话的view
YCOrderRecordView :这个是下单中按住说话的view跟聊天系统没关系
YCPlayButton :播放音频按钮
YCChatDriverAcceptCardCell :5.2.3版本新增预订成功卡片
YCChatJourneyStartCell : 5.2.3版本新增司机已出发、司机已就位卡片
YCChatUpdatHintCell : 不支持类型,升级提示

Controllers:

YCChatViewController :聊天的主界面
YCChatMenuViewController:聊天右侧的按钮
YCShareJourneyCardVC:预订成功卡片的详细页
YCChatMapVC:聊天卡片里的地图
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值