如果是使用OpenIMCore集成,IMSDK为你提供消息通道的能力,即消息发送和消息接收。
你可以基于消息通道的能力,自己完成消息的发送、存储、显示等过程,继而为用户提供IM沟通的能力。
要使用消息通道的基础能力,你只需要关注下面这几个步骤:初始化、登录、发消息、收消息等。
/** * 初始化示例代码 */ - (BOOL)exampleInit; { /// 设置环境 [[YWAPI sharedInstance] setEnvironment:YWEnvironmentRelease]; /// 开启日志 [[YWAPI sharedInstance] setLogEnabled:YES]; NSLog(@"SDKVersion:%@", [YWAPI sharedInstance].YWSDKIdentifier); NSError *error = nil; /// 异步初始化IM SDK #warning TODO: CHANGE TO YOUR AppKey [[YWAPI sharedInstance] syncInitWithOwnAppKey:@"23015524" getError:&error]; if (error.code != 0 && error.code != YWSdkInitErrorCodeAlreadyInited) { /// 初始化失败 return NO; } else { if (error.code == 0) { /// 首次初始化成功 /// 获取一个IMCore并持有 self.IMCore = [[YWAPI sharedInstance] fetchNewIMCoreForOpenIM]; } else { /// 已经初始化 } return YES; } }
syncInitWithOwnAppKey:getError:
方法进行初始化【YWAPI.h】fetchNewIMCoreForOpenIM
接口获取并保存YWIMCore的实例,你可以使用YWIMCore的实例建立消息通道。【YWAPI.h】IMSDK的登录有几个概念:
预登录
预登录的作用,是为了能在登录成功之前就打开本地数据库,为用户展示本地聊天数据。
所以一般你可以根据业务的需要,在允许用户查看聊天数据时,就使用调用预登录的接口,如果预登录成功,即可显示本地数据。
真正登录
无论预登录是否成功,你都需要发起真正登录,才能与IM服务器建立连接,收发IM消息。
请参考:官方demo中callThisAfterISVAccountLoginSuccessWithYWLoginId:passWord:preloginedBlock:successBlock:failedBlock:
方法。
登录时机【重要,务必理解下述内容】:
asyncLoginWithCompletionBlock:
接口发起登录,IMSDK不会做自动登录。/** * 预登陆 */ - (void)examplePreLoginWithLoginId:(NSString *)loginId successBlock:(void(^)())aPreloginedBlock { /// 预登录 if ([[self.ywIMKit.IMCore getLoginService] preLoginWithPerson:[[YWPerson alloc] initWithPersonId:loginId]]) { /// 预登录成功,直接进入页面,这里可以打开界面 if (aPreloginedBlock) { aPreloginedBlock(); } } }
/** * 登录的示例代码 */ - (void)exampleLoginWithUserID:(NSString *)aUserID password:(NSString *)aPassword successBlock:(void(^)())aSuccessBlock failedBlock:(void (^)(NSError *))aFailedBlock { aSuccessBlock = [aSuccessBlock copy]; aFailedBlock = [aFailedBlock copy]; /// 登录之前,先告诉IM如何获取登录信息。 /// 当IM向服务器发起登录请求之前,会调用这个block,来获取用户名和密码信息。 [[self.ywIMKit.IMCore getLoginService] setFetchLoginInfoBlock:^(YWFetchLoginInfoCompletionBlock aCompletionBlock) { aCompletionBlock(YES, aUserID, aPassword, nil, nil); }]; /// 发起登录 [[self.ywIMKit.IMCore getLoginService] asyncLoginWithCompletionBlock:^(NSError *aError, NSDictionary *aResult) { if (aError.code == 0 || [[self.ywIMKit.IMCore getLoginService] isCurrentLogined]) { /// 登录成功 #ifdef DEBUG [[SPUtil sharedInstance] showNotificationInViewController:self.rootWindow.rootViewController title:@"登录成功" subtitle:nil type:SPMessageNotificationTypeSuccess]; #endif if (aSuccessBlock) { aSuccessBlock(); } } else { /// 登录失败 [[SPUtil sharedInstance] showNotificationInViewController:self.rootWindow.rootViewController title:@"登录失败" subtitle:aError.description type:SPMessageNotificationTypeError]; if (aFailedBlock) { aFailedBlock(aError); } } }]; }
setFetchLoginInfoBlock:
设置获取登录信息的回调。一般地,你需要在这个回调里先获取该用户的昵称,成功后再调用aCompletionBlock
,将userId、password、displayName等信息告知IMSDK。(昵称用于对方收到系统推送时显示)【IYWLoginService.h】asyncLoginWithCompletionBlock:
真正发起登录【IYWLoginService.h】注意: 目前要求登录操作,必须在主线程调用(底层CoreData的NSFetchedResultsController需要在主线程创建才能在主线程回调),否则一些回调比如未读数变更的回调,将会无效。
你可以通过IYWLoginService.h
中的- (BOOL)hasLoginThread
函数判断是否已经发起登录。
- (void)sendImageMessageData:(NSData *)imageData { /// 构造消息体 YWMessageBodyImage *imageMessageBody = [[YWMessageBodyImage alloc] initWithMessageImageData:imageData]; /// 获取会话 YWP2PConversation *conv = [YWP2PConversation fetchConversationByPerson:[[YWPerson alloc] initWithPersonId:@"uid1"] creatIfNotExist:YES baseContext:self.IMCore]; /// 发送消息 [conv asyncSendMessageBody:imageMessageBody controlParameters:nil progress:^(CGFloat progress, NSString *messageID) { /// 更新消息进度显示 } completion:^(NSError *error, NSString *messageID) { if (error) { /// 消息发送失败,提示用户 } }]; }
消息发送控制
asyncSendMessageBody:progress:completion:
方法发送消息。[[self.IMCore getConversationService] addOnNewMessageBlockV2:^(NSArray *aMessages, BOOL aIsOffline) { [aMessages enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) { id<IYWMessage> message = [obj conformsToProtocol:@protocol(IYWMessage)] ? obj : nil; if (message) { /// 处理消息 } }]; } forKey:self.description ofPriority:YWBlockPriorityDeveloper];
aMessages
数组中的每一个对象都是遵循IYWMessage
协议的对象,你可以从中获取到消息的具体信息。目前账号设置支持单聊、群聊的消息接收设置和开发者自定义设置,方法声明在YWIMCore
层中的IYWSettingService
中,这个头文件中的方法分为同步方法和异步方法,同步方法只获取本地的数据,异步方法则需要发送网络请求。
开发者可以通过
/** * 将本地的消息接收设置更新至服务端的状态,sdk内部每24h同步一次,开发者可以视自己需求调用 */ - (void)syncMessageSettingFromServerWithompletionBlcok:(YWCompletionBlock)aCompletion;
来同步服务端的设置,同步成功后开发者可以在aCompletion
中通过调用同步方法来获取本地的数据。
单聊消息可以设置在后台时消息是否PUSH,屏蔽单聊消息则是通过更多功能中的黑名单来实现的。
/// 获取person 发送的消息后台是否push - (BOOL)getMessagePushEnableForPerson:(YWPerson *)person; /// 异步设置person 发送的消息后台是否push - (void)asyncSetMessagePushEnable:(BOOL)enable ForPerson:(YWPerson *)person completion:(YWCompletionBlock)aCompletion;
群聊消息可以分别设置群普通消息的接收和群@消息的接收方式,需要注意的是,群普通消息分为接收并PUSH、接收不PUSH和不接收三种,而群@消息只有接收并PUSH和不接收两种,其取值参考IYWSettingServiceDef
中的YWMessageFlag
和YWAtMessageFlag
两个枚举:
typedef NS_ENUM(NSInteger, YWMessageFlag) { YWMessageFlagNotReceive, // 屏蔽消息 YWMessageFlagReceiveButNoAlert, // 前台在线时接收消息,后台不push YWMessageFlagReceive, // 正常接收消息,后台有push }; typedef NS_ENUM(NSInteger, YWAtMessageFlag) { YWAtMessageFlagNotReceive, // 屏蔽消息 YWAtMessageFlagReceive, // 正常接收消息,后台有push };
群消息设置的方法较多,开发者可以移步IYWSettingService
头文件中查看。
云旺提供了开发者保存自定义设置的功能,客户端和服务器均为透传,当开发者需要多端同步自定义设置时,可以通过
/** * 获取开发者自定义的设置项 */ - (NSDictionary *)getExtraSettings; /** * 异步设置自定义设置项,服务端只负责保存,不会进行解析 */ - (void)asyncSetExtraSettings:(NSDictionary *)settings completion:(YWCompletionBlock)aCompletion;
来获取和设置。
IMSDK提供了消息发送控制的能力,你利用YWConversation.h
中消息发送接口携带的controlParameters
参数,控制对方iOS设备接收消息的推送文案、推送声音、是否需要推送等,也可以控制消息仅存本地而不发送到对方。
如下面的代码所示,通过controlParameters参数,控制了对方收到推送的推送文案,你还可以控制是否弹出Push、Push的声音等等,详见:YWConversationServiceDef.h
/** * 发送透传指令 * 并且展示了如何在客户端控制对方iOS设备收到的Push文案 * 不显示在会话列表和聊天页面,开发者可以监听到该消息,做特定的逻辑处理 */ - (void)exampleSendTransparentCommand:(NSString *)aCommand inConversation:(YWConversation *)aConversation completion:(YWMessageSendingCompletionBlock)aCompletion { YWMessageBodyCustomize *body = [[YWMessageBodyCustomize alloc] initWithMessageCustomizeContent:aCommand summary:@"阅后即焚" isTransparent:YES]; /// 控制对方收到的Push文案,你还可以控制推送声音,是否需要push等,详见:YWConversationServiceDef.h NSDictionary *controlParameters = @{kYWMsgCtrlKeyPush:@{kYWMsgCtrlKeyPushKeyHowToPush:@{kYWMsgCtrlKeyPushKeyHowToPushKeyTitle:@"请务必阅后即焚"}}}; [aConversation asyncSendMessageBody:body controlParameters:controlParameters progress:NULL completion:aCompletion]; }
/**消息控制字段定义**/ /** **示例** @{ kYWMsgCtrlKeyPush:@{ kYWMsgCtrlKeyPushKeyNeedPush:@(1), kYWMsgCtrlKeyPushKeyHowToPush:@{ kYWMsgCtrlKeyPushKeyHowToPushKeyTitle:@"推送文案自定义", kYWMsgCtrlKeyPushKeyHowToPushKeySound:@"customsound.caf", kYWMsgCtrlKeyPushKeyHowToPushKeyData:@"推送透传数据", }, }, kYWMsgCtrlKeyClientLocal:@{kYWMsgCtrlKeyClientLocalKeyOnlySave:@(YES)}, /// 本地保存 } ******* */ /// 推送控制 /// 值一个字典,控制推送。该字典包含:kYWMsgCtrlKeyPushKeyNeedPush、kYWMsgCtrlKeyPushKeyHowToPush FOUNDATION_EXTERN NSString *const kYWMsgCtrlKeyPush; /// 值为一个整数,控制是否推送:没有这个key为需要推送,@(1)为需要推送,@(0)为不需要推送 FOUNDATION_EXTERN NSString *const kYWMsgCtrlKeyPushKeyNeedPush; /// 值为一个字典,控制推送样式。该字典包含:kYWMsgCtrlKeyPushKeyHowToPushKeyTitle、kYWMsgCtrlKeyPushKeyHowToPushKeySound、kYWMsgCtrlKeyPushKeyHowToPushKeyData FOUNDATION_EXTERN NSString *const kYWMsgCtrlKeyPushKeyHowToPush; /// 值为字符串,控制推送文案 FOUNDATION_EXTERN NSString *const kYWMsgCtrlKeyPushKeyHowToPushKeyTitle; /// 值为字符串,控制推送声音文件 FOUNDATION_EXTERN NSString *const kYWMsgCtrlKeyPushKeyHowToPushKeySound; /// 值为字符串,控制推送携带的数据 FOUNDATION_EXTERN NSString *const kYWMsgCtrlKeyPushKeyHowToPushKeyData; /// 值为一个字典,控制客户端本地行为。该字典包含:kYWMsgCtrlKeyClientLocalKeyOnlySave FOUNDATION_EXTERN NSString *const kYWMsgCtrlKeyClientLocal; /// 值为一个bool值,控制该消息仅保存本地,不发送到服务器。无此key或者@(0)则为需要发送,@(1)则为仅保存本地 FOUNDATION_EXTERN NSString *const kYWMsgCtrlKeyClientLocalKeyOnlySave; /// 值为一个bool值,控制该消息保存本地时即标记删除,但仍然发送到服务器,本地因为被标记删除所以不会显示。无此key或者@(0)则为不标记删除,@(1)则为标记删除 FOUNDATION_EXTERN NSString *const kYWMsgCtrlKeyClientLocalKeyMarkDeleted;
下面的代码展示了使用controlParameters字段控制消息不发送给对方,只在本地展示
/** * 插入本地消息 * 消息不会被发送到对方,仅本地展示 */ - (void)exampleInsertLocalMessageBody:(YWMessageBody *)aBody inConversation:(YWConversation *)aConversation { NSDictionary *controlParameters = @{kYWMsgCtrlKeyClientLocal:@{kYWMsgCtrlKeyClientLocalKeyOnlySave:@(YES)}}; /// 控制字段 [aConversation asyncSendMessageBody:aBody controlParameters:controlParameters progress:NULL completion:NULL]; }
kYWMsgCtrlKeyClientLocalKeyMarkDeleted
,并设置为@(YES)示例如下:
YWConversationViewController *controller = [YWConversationViewController makeControllerWithIMKit:self.imkit conversation:conversation]; __weak typeof(controller) weakController = controller; __weak typeof(self) weakSelf = self; [controller setViewDidLoadBlock:^{ BOOL needToSayHi = ...; /// 根据你的需要,确定是否需要自动打招呼。一般需要判断,对方是否客服以及最近一天是否已经和客服打过招呼 NSString *hiContent = @"Hello"; /// 根据你的需要设置合适的招呼内容 if (needToSayHi) { [weakController.conversation asyncSendMessageBody:[[YWMessageBodyText alloc] initWithMessageText:hiContent] controlParameters:@{kYWMsgCtrlKeyClientLocal:@{kYWMsgCtrlKeyClientLocalKeyMarkDeleted:@(YES)}} progress:NULL completion:NULL]; } }];
消息透传指令用于在当前客户端发送一条消息给对方,对方收到后,不会在聊天页面或者会话页面有任何展示。你可以根据需要,监听该消息,并处理相关逻辑。
下面的代码用透传指令演示了阅后即焚的功能,对方收到消息后,展示一个弹框。(你也可以不做任何提示,仅做逻辑处理)。
/** * 发送透传指令 * 并且展示了如何在客户端控制对方iOS设备收到的Push文案 * 不显示在会话列表和聊天页面,开发者可以监听到该消息,做特定的逻辑处理 */ - (void)exampleSendTransparentCommand:(NSString *)aCommand inConversation:(YWConversation *)aConversation completion:(YWMessageSendingCompletionBlock)aCompletion { YWMessageBodyCustomize *body = [[YWMessageBodyCustomize alloc] initWithMessageCustomizeContent:aCommand summary:@"阅后即焚" isTransparent:YES]; /// 控制对方收到的Push文案,你还可以控制推送声音,是否需要push等,详见:YWConversationServiceDef.h NSDictionary *controlParameters = @{kYWMsgCtrlKeyPush:@{kYWMsgCtrlKeyPushKeyNeedPush:@(0)}}; [aConversation asyncSendMessageBody:body controlParameters:controlParameters progress:NULL completion:aCompletion]; }
你需要监听所有消息,并可以对透传消息做特定的处理。下面的代码监听了所有消息,当消息体是透传指令时,展示一个弹框。
/** * 监听新消息 */ - (void)exampleListenNewMessage { [[self.ywIMKit.IMCore getConversationService] addOnNewMessageBlockV2:^(NSArray *aMessages, BOOL aIsOffline) { /// 你可以在此处根据需要播放提示音 /// 展示透传消息 [aMessages enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { id<IYWMessage> msg = obj; YWMessageBodyCustomize *body = nil; if ([msg respondsToSelector:@selector(messageBody)]) { body = [[msg messageBody] isKindOfClass:[YWMessageBodyCustomize class]] ? (YWMessageBodyCustomize *)[msg messageBody] : nil; } if (body) { NSData *contentData = [body.content dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *contentDictionary = [NSJSONSerialization JSONObjectWithData:contentData options:0 error:NULL]; NSString *messageType = contentDictionary[@"customizeMessageType"]; if ([messageType isEqualToString:@"yuehoujifen"] && body.isTransparent) { NSString *text = contentDictionary[@"Text"]; if (text.length > 0) { dispatch_async(dispatch_get_main_queue(), ^{ UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"阅后即焚" message:text delegate:nil cancelButtonTitle:@"朕知道了" otherButtonTitles:nil]; [av show]; }); } } } }]; } forKey:self.description ofPriority:YWBlockPriorityDeveloper]; }
对于每一条发出的消息,IMSDK均会给予回调,通过如下的代码添加监听,你需要实现YWMessageLifeDelegate
协议:
/** * 监听自己发送的消息的生命周期 */ - (void)exampleListenMyMessageLife { [[self.ywIMKit.IMCore getConversationService] addMessageLifeDelegate:self forPriority:YWBlockPriorityDeveloper]; }
你可以通过消息发送前的回调,进行修改消息内容、修改会话或者干脆控制不发出该消息。(影响所有消息,请务必谨慎调试)。
下面的代码展示了,所发送的消息包含违禁词语时,改为插入一条本地消息。controlParameters
的定义与前面消息发送控制中的controlParameters
一致。
/// 当你监听了消息生命周期,IMSDK会回调以下两个函数 - (YWMessageLifeContext *)messageLifeWillSend:(YWMessageLifeContext *)aContext { /// 你可以通过返回context,来实现改变消息的能力 if ([aContext.messageBody isKindOfClass:[YWMessageBodyText class]]) { NSString *text = [(YWMessageBodyText *)aContext.messageBody messageText]; if ([text rangeOfString:@"法轮功"].location != NSNotFound) { YWMessageBodySystemNotify *bodyNotify = [[YWMessageBodySystemNotify alloc] initWithContent:@"消息包含违禁词语"]; [aContext setMessageBody:bodyNotify]; NSDictionary *params = @{kYWMsgCtrlKeyClientLocal:@{kYWMsgCtrlKeyClientLocalKeyOnlySave:@(YES)}}; [aContext setControlParameters:params]; return aContext; } } return nil; }
- (void)messageLifeDidSend:(NSString *)aMessageId conversationId:(NSString *)aConversationId result:(NSError *)aResult { /// 你可以在消息发送完成后,做一些事情,例如播放一个提示音等等 }