iOS基于Bmob的实时通讯

最近刚完成一个基于Bmob后台的项目,用到了实时通讯这一功能,完成它还真是一个浩大的工程呐,一些步骤及重点在这里记录下来

下载安装BmobIMSDK

首先我们要通过官网下载最新的BmobIMSDK,将它导到自己的项目工程里面

引入相关的库文件

如果项目中已包含BmobSDK数据服务SDK的话,可以不添加新的框架,如果没有则需添加SystemConfiguration.framework、CoreFoundation.framework、Foundation.framework、CFNetwork.framwork、CoreGraphics.framework、sqlite3.tbd

初始化

在AppDelegate.m文件中,引入相关的头文件, 填入申请的授权Key(SDK使用的是应用密钥里的Application ID)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //初始化BmobSDK
    [Bmob registerWithAppKey:@"Application ID"];

    self.sharedIM = [BmobIM sharedBmobIM];

    [self.sharedIM registerWithAppKey:@"Application ID"];

    [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;

    BmobUser *user = [BmobUser getCurrentUser];
    //如果有用户了,不需要推送服务来推送消息的话,可以直接连接服务器
    if (user) {
        self.userId = user.objectId;
        [self connectToServer];
    }else{
        //如果用户还未登录,则监听对应的通知,再进行处理
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userLogin:) name:@"Login" object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userLogout:) name:@"Logout" object:nil];
    }

    self.sharedIM.delegate = self;
    return YES;
}
如果需要推送服务的话,可以在其相关代理那里设置调用 [self.sharedIM setupDeviceToken:@"xxxx"]的方法后,在连接服务器,当然需要在管理后台上传对应Bundle ID的p12文件,请勿加密

-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(nonnull NSError *)error{
    BmobUser *user = [BmobUser getCurrentUser];
    if (user) {
        [self connectToServer];
    }
}
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
        BmobUser *user = [BmobUser getCurrentUser];
    if (user) {
        //将deviceToken转成字符串
        NSString *string = [[NSString alloc] initWithData:deviceToken encoding:NSUTF8StringEncoding];
        self.token = [[[string stringByReplacingOccurrencesOfString:@" " withString:@""] stringByReplacingOccurrencesOfString:@"<" withString:@""] stringByReplacingOccurrencesOfString:@">" withString:@""];
        [self connectToServer];           
    }
}
通知方法:

登陆对应处理

-(void)userLogin:(NSNotification *)noti{
    NSString *userId = noti.object;
    self.userId = userId;
    [self connectToServer];
}
退出登录的时候需要断开连接
-(void)userLogout:(NSNotification *)noti{
    [self.sharedIM disconnect];
}
连接服务器
-(void)connectToServer{
    [self.sharedIM setupBelongId:self.userId];
    [self.sharedIM setupDeviceToken:self.token];
    [self.sharedIM connect];
}
在应用进入前台或者后台的时候可以重新进行连接或者断开连接
进入前台
- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.

    if (self.userId && self.userId.length > 0) {
        [self connectToServer];
    }
}
进入后台
- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    if ([self.sharedIM isConnected]) {
        [self.sharedIM disconnect];
    }
需要说明的是,连接服务器建立一次即可,开发者自己控制连接服务器的时机。建立连接之前必须设置appKey和belongId,不然会抛出异常

注册推送

可以在函数 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 添加推送的相关代码

CGFloat version = [[[UIDevice currentDevice] systemVersion] floatValue];
    if (version >= 10.0)
    {
        //iOS 10推送
        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
        [center requestAuthorizationWithOptions:UNAuthorizationOptionCarPlay | UNAuthorizationOptionSound | UNAuthorizationOptionBadge | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
            
            if (granted) {
                NSLog(@" iOS 10 request notification success");
            }else{
                NSLog(@" iOS 10 request notification fail");
            }
        }];
    } else if (version >= 8.0) {
        //iOS 8、9推送
        UIMutableUserNotificationCategory *categorys = [[UIMutableUserNotificationCategory alloc]init];
        //注意:此处的Bundle ID要与你申请证书时填写的一致。
        categorys.identifier=@"Bmob";
        
        UIUserNotificationSettings *userNotifiSetting = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound) categories:[NSSet setWithObjects:categorys,nil]];
        
        [[UIApplication sharedApplication] registerUserNotificationSettings:userNotifiSetting];
        
        [[UIApplication sharedApplication] registerForRemoteNotifications];
    }

接收消息

在初始化的部分,我们已经设置了self.sharedIM的delegate,我们只需要在对应的代理函数那里处理相关的应用逻辑即可。
在应用连接服务器的时候,SDK会主动的获取离线消息,并保存到本地数据库里面。在这里,开发者可能需要知道哪些人发来了消息,并且去下载相关人物信息。
-(void)didGetOfflineMessagesWithIM:(BmobIM *)im{
    //获取哪些人的消息还未读
    NSArray *objectIds = [self.sharedIM allConversationUsersIds];
    if (objectIds && objectIds.count > 0) {
        //查找服务器相关人物的信息
        [UserService loadUsersWithUserIds:objectIds completion:^(NSArray *array, NSError *error) {
            if (array && array.count > 0) {
                //保存到本地数据库
                [self.sharedIM saveUserInfos:array];
                //发新用户的通知
                [[NSNotificationCenter defaultCenter] postNotificationName:kNewMessageFromer object:nil];
            }
        }];
    }
}
如果已经连接到服务器上了,就可以收到别人发送过来的消息,这是需要在另一个方法处理
-(void)didRecieveMessage:(BmobIMMessage *)message withIM:(BmobIM *)im{
    //查看本地有无这个用户的信息
    BmobIMUserInfo *userInfo = [self.sharedIM userInfoWithUserId:message.fromId];
    if (!userInfo) {
          //如果没有则去下载
        [UserService loadUserWithUserId:message.fromId completion:^(BmobIMUserInfo *result, NSError *error) {
            if (result) {
                //保存到本地数据库
                [self.sharedIM saveUserInfo:result];
                //发新用户的通知
                [[NSNotificationCenter defaultCenter] postNotificationName:kNewMessageFromer object:nil];
            }
            //发接收到新信息的通知
            [[NSNotificationCenter defaultCenter] postNotificationName:kNewMessagesNotifacation object:message];
        }];
    }else{
          //发接收到新信息的通知
        [[NSNotificationCenter defaultCenter] postNotificationName:kNewMessagesNotifacation object:message];
    }
}
注册

BmobUser *user = [[BmobUser alloc] init];
user.username = self.usernameTextField.text;
user.password = self.passwordTextField.text;
[user signUpInBackgroundWithBlock:^(BOOL isSuccessful, NSError *error) {
     if (isSuccessful) {
         [[NSNotificationCenter defaultCenter] postNotificationName:@"Login" object:user.objectId];
         [self dismissViewControllerAnimated:YES completion:nil];
     }else{
         [self showInfomation:error.description];
     }
}];

登陆
[self showLoading];
    [BmobUser loginWithUsernameInBackground:self.usernameTextField.text password:self.passwordTextField.text block:^(BmobUser *user, NSError *error) {
        if (user) {
            [self hideLoading];
            [[NSNotificationCenter defaultCenter] postNotificationName:@"Login" object:user.objectId];
            [self dismissViewControllerAnimated:YES completion:nil];
        }else{
            [self showInfomation:error.description];
        }
    }];
获取好友列表
-(void)loadUserFriends{
    [UserService friendsWithCompletion:^(NSArray *array, NSError *error) {
        if (error) {
            [self showInfomation:error.localizedDescription];
        }else{
            BmobUser *loginUser = [BmobUser getCurrentUser];
            NSMutableArray *result  = [NSMutableArray array];
            for (BmobObject *obj in array) {

                BmobUser *friend = nil;
                if ([[(BmobUser *)[obj objectForKey:@"user"] objectId] isEqualToString:loginUser.objectId]) {
                    friend = [obj objectForKey:@"friendUser"];
                }else{
                    friend = [obj objectForKey:@"user"];
                }
                BmobIMUserInfo *info = [BmobIMUserInfo userInfoWithBmobUser:friend];

                [result addObject:info];
            }
            if (result && result.count > 0) {
                [self.userArray setArray:result];
                [self.tableView reloadData];

            }

        }
    }];
}
添加好友
-(void)addFriend{
    [UserService addFriendNoticeWithUserId:self.userInfo.userId completion:^(BOOL isSuccessful, NSError *error) {
        if (error) {
            [self showInfomation:error.localizedDescription];
        }else{
            [self showInfomation:@"已发送添加好友请求"];
        }
    }];
}

消息发送
通过IMSDK,用户可以与陌生人聊天,也可以与好友聊天,这个由我们控制。当需要发起聊天的时候,需要建立起一个BmobIMConversation对象来进行管理,SDK提供了方法来快速构建BmobIMConversation对象。
BmobIMConversation *conversation = [BmobIMConversation conversationWithId:self.userInfo.userId conversationType:BmobIMConversationTypeSingle];
conversation.conversationTitle = self.userInfo.name;//这是聊天对象名,显示到title上
查看聊天记录
加载第一页数据的时候,只需要将message设置为nil
-(void)loadMessageRecords{
    NSArray *array = [self.conversation queryMessagesWithMessage:nil limit:10];//每次显示10条数据

    if (array && array.count > 0) {
        //根据消息更新时间进行排序
        NSArray *result = [array sortedArrayUsingComparator:^NSComparisonResult(BmobIMMessage *obj1, BmobIMMessage *obj2) {
            if (obj1.updatedTime > obj2.updatedTime) {
                return NSOrderedDescending;
            }else if(obj1.updatedTime <  obj2.updatedTime) {
                return NSOrderedAscending;
            }else{
                return NSOrderedSame;
            }

        }];
        [self.messagesArray setArray:result];
        [self.tableView reloadData];

        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.messagesArray.count-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
    }
}
加载之前的历史消息记录,可以通过将message参数设置为时间为最后一条消息即可
-(void)loadMoreRecords{
    if (!self.finished) {
        self.page ++;
        [self.freshControl beginRefreshing];

        if (self.messagesArray.count <= 0) {
            [self.freshControl endRefreshing];
            return;
        }
        BmobIMMessage *msg = [self.messagesArray firstObject];

        NSArray *array = [self.conversation queryMessagesWithMessage:msg limit:10];

        if (array && array.count > 0) {
            NSMutableArray *messages = [NSMutableArray arrayWithArray:self.messagesArray];
            [messages addObjectsFromArray:array];
            //排序
            NSArray *result = [messages sortedArrayUsingComparator:^NSComparisonResult(BmobIMMessage *obj1, BmobIMMessage *obj2) {
                if (obj1.updatedTime > obj2.updatedTime) {
                    return NSOrderedDescending;
                }else if(obj1.updatedTime <  obj2.updatedTime) {
                    return NSOrderedAscending;
                }else{
                    return NSOrderedSame;
                }

            }];
            [self.messagesArray setArray:result];
            [self.tableView reloadData];
        }else{
            self.finished = YES;
            [self showInfomation:@"没有更多的历史消息"];
        }

    }else{
        [self showInfomation:@"没有更多的历史消息"];
    }

    [self.freshControl endRefreshing];
}

发送消息

发送文本消息
-(void)sendTextWithTextField:(UITextField *)textField{
    if (textField.text.length == 0) {
        [self showInfomation:@"请输入内容"];
    }else{
        //创建BmobIMTextMessage对象
        BmobIMTextMessage *message = [BmobIMTextMessage messageWithText:textField.text attributes:nil];
         //聊天类型设置为单聊
        message.conversationType =  BmobIMConversationTypeSingle;
        message.createdTime = (uint64_t)([[NSDate date] timeIntervalSince1970] * 1000);
        message.updatedTime = message.createdTime;
        [self.messagesArray addObject:message];
        [self.tableView reloadData];
        self.bottomView.textField.text = nil;
        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.messagesArray.count-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
        __weak typeof(self)weakSelf = self;
        [self.conversation sendMessage:message completion:^(BOOL isSuccessful, NSError *error) {
            [weakSelf.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.messagesArray.count-1 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];

        }];

    }
}
发送图片

发送图片消息的流程是通过把拍照或者图片的内容,转成Data,然后通过Bmob文件上传接口来上传文件,获取到相关的信息(文件地址之类的),再构造对应的BmobIMImageMessage对象来发送消息。
这里封装一个直接传入image就能发送图片的方法
+(void)uploadImage:(UIImage *)image
        completion:(uploadBlock)block
          progress:(BmobProgressBlock)progressBlock
                          {
    BmobFile *file = [[BmobFile alloc] initWithFileName:[NSString stringWithFormat:@"%.0f.png",[[NSDate date] timeIntervalSinceReferenceDate] ] withFileData:UIImagePNGRepresentation(image)];
    
    [file saveInBackgroundByDataSharding:^(BOOL isSuccessful, NSError *error) {
        BmobIMImageMessage *message = nil;
        if (!error) {
          message = [BmobIMImageMessage messageWithUrl:file.url attributes:@{KEY_METADATA:@{KEY_HEIGHT:@(image.size.height),KEY_WIDTH:@(image.size.width)}}];
            message.conversationType = BmobIMConversationTypeSingle;
            message.createdTime = (uint64_t)([[NSDate date] timeIntervalSince1970] * 1000);
            message.updatedTime = message.createdTime;
        }
        if (block) {
            block(message,error);
        }
    } progressBlock:^(CGFloat progress) {
        if (progressBlock) {
            progressBlock(progress);
        }
    }];
}
然后可以直接调用方法来发送图片,并在当前页显示出来
[MessageService uploadImage:resizeImage completion:^(BmobIMImageMessage *message, NSError *error) {
        if (!error) {
            [self.messagesArray addObject:message];
            [self scrollToBottom];
             __weak typeof(self)weakSelf = self;

            [self.conversation sendMessage:message completion:^(BOOL isSuccessful, NSError *error) {
                [weakSelf reloadLastRow];
            }];
        }else{
            [self showInfomation:error.localizedDescription];
        }
    } progress:^(CGFloat progress) {
        [self showProgress:progress];
    }];
发送语音文件(模拟器上不能发送语音,只有真机上才可以操作,还需要设置访问录音的白名单)

发送语音消息的流程是把录音下载的Data转成AMR格式,保存在本地,然后通过Bmob文件上传接口来上传文件,获取到相关的信息(文件地址之类的),再构造对应的BmobIMAudioMessage对象来发送消息。
封装一个直接传入NSData就能发送语音文件的方法
+(void)uploadAudio:(NSData *)data
                          duration:(CGFloat)duration
                        completion:(uploadBlock)block
                          progress:(BmobProgressBlock)progressBlock
                           {
    //保存在本地
    NSString *filename = [NSString stringWithFormat:@"%.0f.amr",[NSDate timeIntervalSinceReferenceDate]];
    NSString *path = [[CommonUtil audioCacheDirectory] stringByAppendingPathComponent:filename];
    [data writeToFile:path options:NSDataWritingAtomic error:nil];
    

    
    BmobFile *file = [[BmobFile alloc] initWithFileName:filename withFileData:data];
    [file saveInBackground:^(BOOL isSuccessful, NSError *error) {
         BmobIMAudioMessage *message = nil;
        if (!error) {
            message = [BmobIMAudioMessage messageWithUrl:file.url attributes:@{KEY_METADATA:@{KEY_DURATION:@(duration)}}];
            message.conversationType = BmobIMConversationTypeSingle;
            message.createdTime = (uint64_t)([[NSDate date] timeIntervalSince1970] * 1000);
            message.updatedTime = message.createdTime;
            message.localPath   = [path stringByReplacingOccurrencesOfString:NSHomeDirectory() withString:@""];
        }
        if (block) {
            block(message,error);
        }
    } withProgressBlock:^(CGFloat progress) {
        if (progressBlock) {
            progressBlock(progress);
        }
    }];
}
直接调用这个方法就能发送语音文件
[MessageService uploadAudio:data
                           duration:duration
                         completion:^(BmobIMAudioMessage *message, NSError *error) {
                             if (!error) {

                                 [self.messagesArray addObject:message];
                                 [self scrollToBottom];
                                 __weak typeof(self)weakSelf = self;
                                 [self.conversation sendMessage:message completion:^(BOOL isSuccessful, NSError *error) {
                                     [weakSelf reloadLastRow];
                                 }];
                             }
                         } progress:nil];

接收消息

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveMessage:) name:kNewMessagesNotifacation object:nil];
-(void)receiveMessage:(NSNotification *)noti{
    BmobIMMessage *message = noti.object;
    //如果是消息来源是当前聊天用户,就将其加载到内存里并显示出来
    if ([message.fromId isEqualToString:self.conversation.conversationId]) {
        [self.messagesArray addObject:message];
        [self.tableView reloadData];
        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.messagesArray.count-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
    }

}
在这里的描述不是特别详细,因为这是一个特别大的模块,所以一些小的细节并没有展示出来,以及运用tableview来展示聊天的排版,通过自己与好友的id区分左右排版,通过id获得用户的信息展示,项目中的截图:






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值