# IOS 客户端快速开发

本页面供快速集成使用，了解更多请访问[详细文档](/reference/floo-ios.md)

## 前期准备

美信拓扑 IM SDK 提供两种集成方式，可以通过 CocoaPods 自动集成我们的 floo-ios，也可以通过手动下载 floo-ios.framework, 手动添加到项目中。

### 方式一：自动集成/CocoaPods

提示：如果未安装cocoapods，请参照 [CocoaPods安装](https://cocoapods.org)

1. 在 Podfile 文件中加入 floo-ios :

   ```
   pod 'floo-ios'
   ```
2. 执行安装 ，命令如下

   ```
   pod install
   ```

   提示：如果无法安装 SDK 最新版本，运行以下命令更新本地的 CocoaPods 仓库列表

   ```
   pod repo update
   ```

### 方式二：手动集成

1. [下载 floo-ios.framework](https://package.maximtop.com/floo-ios.zip) , 然后将文件引用到您的项目中。
2. 添加系统库依赖

   您除了在工程中导入 SDK 之前，还需要添加如下系统库的引用。

   * libsqlite3.tbd
   * libc++abi.tbd
   * libsqlite3.tbd
   * libstdc++.tbd
   * libz.tbd
   * libc++.tbd
   * libresolv.tbd
   * libcrypto.a
   * UIKit.framework
   * QuartzCore.framework
   * ImageIO.framework
   * CoreVideo.framework
   * CoreMedia.framework
   * CoreGraphics.framework
   * AVFoundation.framework
   * AssetsLibrary.framework

   注意：

   如果您的工程内已经引用libcrypto.a，为避免引入冲突不需要再次导入工程。

   如果您的工程内没有引用libcrypto.a，请解压下载的SDK包将libcrypto.a导入工程。
3. 在 Xcode 项目 Build Settings - Other Linker Flags 中，增加 "-ObjC"。
4. 设置 App 支持 HTTPS
5. 推送证书制作

## 快速集成

### 一、初始化

在您需要使用MaxIM SDK 功能的类中，import 相关头文件。

```
#import "BMXClient.h"
```

您在使用MaxIM SDK 所有功能之前，您必须先调用此方法初始化 SDK。 在 App 的整个生命周期中，您只需要将 SDK 初始化一次。

```
// 设置存储路径

NSString* dataDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@“ChatData”];

NSString* cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject stringByAppendingString:@“UserCache”];

// 初始化

BMXSDKConfig *config = [[BMXSDKConfig alloc] initConfigWithDataDir:dataDir cacheDir:cacheDir pushCertName:@“Your APNS Certification” userAgent:@“userAgent”];

config.appID = @“Your AppID”;

[[BMXClient sharedClient] registerWithSDKConfig:config];
```

### 二、注册用户

通过 BMXClient的单例，UserService类，传入 -signUpMobile:password:vertifyCode:userName:方法，注册IM账户。

```
[[[BMXClient sharedClient] userService] signUpMobile:mobile password:password vertifyCode:vertifyCode userName:username completion:^(BMXUserProfile *profile, BMXError *aError) {

        [HQCustomToast hideWating];
        if(!aError) {
             NSLog(@"注册成功");
        }  else {
             NSLog(@"失败 errorCode = %ld ", error.errorCode);
        }
     }];
```

### 三、登录链接服务器

将您在上一步获取到的 账号密码，通过 BMXClient的单例，UserService类，传入 -signInById 方法，即可建立与服务器的连接。

提供两种登录模式：一种是普通手动登录，另一种是快速登录模式

```
[[[BMXClient sharedClient] userService] signInById:userid password:password completion:^(BMXError *error) {
        if (!error) {
            NSLog(@"登录成功 username = %lld , password = %@",userid, password);
          } else {
            NSLog(@"失败 errorCode = %lu ", error.errorCode);
        }
     }];

     // 快速登录,不需要获取token
     [[[BMXClient sharedClient] userService] fastSignInById: userid password:password  completion:^(BMXError *error) {
         if (!error) {
             NSLog(@"登录成功 username = %@ , password = %@", userid, password);
         } else {
             NSLog(@"失败 errorCode = %ld ", error.errorCode);
         }
      }];
```

### 四、会话列表功能

通过 BMXClient的单例，ChatService类，传入 -getAllConversationsWithCompletion 方法，获取所有会话列表。返回BMXConversation对象的数组列表。

如果需要获取多设备同步的离线会话列表，需要在SDK初始化配置loadAllServerConversations属性值为Yes，默认只获取本地会话列表。

```
 [[[BMXClient sharedClient] chatService] getAllConversationsWithCompletion:^(NSArray *conversations) {
    NSLog(@"%ld", conversations.count);
  }];
```

### 五、断开连接

在断开与MaxIM服务器的连接时，默认会停止接收远程推送,会自动解绑设备devicetoken.

```
 [[[BMXClient sharedClient] userService] signOutWithcompletion:^(BMXError *error) {
    if (!error) {
    NSLog(@"退出成功");
    }
 }];
```

## 用户好友

* 添加好友

  ```
     [[[BMXClient sharedClient] rosterService] applyAddRoster:rosterId reason:reason completion:^(BMXRoster *roster, BMXError *error) {
      if (!error) {
      MAXLog(@"申请成功");
    } else {
      MAXLog(@"申请失败");
    }

    }];
  ```
* 删除好友

  ```
    [[[BMXClient sharedClient] rosterService] removeRosterById:@"880" withCompletion:^(BMXError *error) {

    }];
  ```
* 同意添加好友

  ```
    [[[BMXClient sharedClient] rosterService] acceptRosterById:@"880" withCompletion:^(BMXError *error) {
    	 if (!error) {
        MAXLog(@"添加成功");
     }
        MAXLog(@"%@", error);

    }];
  ```
* 拒绝添加好友

  ```
    [[[BMXClient sharedClient] rosterService] declineRosterById:@"880" withReason:reason completion:^(BMXError *error) {

    }];
  ```
* 获取好友列表

  开发者可以通过参数哦forceRefresh,选择从服务器或者是从本地获取好友列表数据。

  如果设置为NO, 当本地数为空，会自动从服务器去获取数据后返回结果。

  ```
    [[[BMXClient sharedClient] rosterService] getRosterListforceRefresh:YES completion:^(NSArray *rostIdList, BMXError *error) {
    if (!error) {
        MAXLog(@"%ld", rostIdList.count);
        [self searchRostersByidArray:[NSArray arrayWithArray:rostIdList]];
    }
    }];
  ```

## 基础功能

### 单聊

单聊是最基本的聊天界面，提供文字、表情、语音片段、图片等多种输入内容，解决 App 内用户的沟通瓶颈。单聊的 BMXConversationType 是 BMXConversationSingle，toId 是单聊对象的 userId。

### 群聊

群组的 BMXConversationType 是 BMXConversationGroup，toId 是群组 Id。

* 创建群组

开发者可以注册监听，创建群组成功后, 收到相应回调通知,开发者可以进行一些UI处理。

```
    // 构建创建群组信息实体
    BMXCreatGroupOption *option = [[BMXCreatGroupOption alloc] initWithGroupName:title groupDescription:description isPublic:YES];
    option.message = message;  // 建群时成员收到的邀请信息
    option.members = ids;   //建群时添加的成员列表
    [[[BMXClient sharedClient] groupService] creatGroupWithCreateGroupOption:option completion:^(BMXGroup *group, BMXError *error) {
        if (!error) {
        }
    }];
```

* 加入群组

  ```
    /**
    加入一个群，根据群设置可能需要管理员批准

    @param group BMXGroup
    @param message 申请信息
    @param aCompletionBlock Error
    */
    - (void)joinGroup:(BMXGroup *)group
      message:(NSString *)message
     completion:(void(^)(BMXError *error))aCompletionBlock;
  ```
* 退出群组

  ```
     [[[BMXClient sharedClient] groupService] leaveGroup:self.group completion:^(BMXError *error) {
        if (!error) {
        }
    }];
  ```
* 解散群组

  ```
     [[[BMXClient sharedClient] groupService] destroyGroup:group completion:^(BMXError *error) {
        if (!error) {
          NSLog(@"销毁群");
      }
    }];
  ```
* 获取群成员列表

  ```
     [[[BMXClient sharedClient] groupService] getMembers:self.group forceRefresh:YES completion:^(NSArray<BMXGroupMember * *groupList, BMXError *error) {
        NSLog(@"%ld", groupList.count);
     }];
  ```
* 获取群组列表

  ```
    	/**
     获取群组列表

     @param forceRefresh 如果设置了forceRefresh则从服务器拉取
     @param aCompletionBlock GroupList, Error
     */
    [[[BMXClient sharedClient] groupService] getGroupListForceRefresh:NO completion:^(NSArray *groupList, BMXError *error) {
    		if (!error) {
      NSLog(@"%ld", groupList.count);
    }
    }];
  ```
* 获取群组信息

  ```
    [[[BMXClient sharedClient] groupService] getGroupInfoByGroupId:self.group.groupId forceRefresh:YES completion:^(BMXGroup *group, BMXError *error) {
     self.group = group;
    }];
  ```

## 消息发送

登录成功之后才能进行聊天操作。发消息时，单聊和群聊调用的是统一接口，区别只是要设置下 BMXConversationType

消息的远程推送：

开发者配置好远程推送的证书，且在代码中申请好权限，并将 deviceToken 传给MaxIM服务器，当接收者不在线的时候，MaxIM服务器会自动通过远程推送将消息发过去。

注： 推送的内容由发送消息接口的 pushContent 字段决定，内置消息发送的时候如果该字段没有值，将使用默认内容推送；自定义消息必须设置该字段，否则将不会推送。

以下是将 deviceToken 传给MaxIM接口

```
[[[BMXClient sharedClient] userService] bindDevice:deviceToken completion:^(BMXError *error) {
      NSLog(@"绑定成功");
 }];
```

### 构建消息实体

### 文本消息

```
 /**
 创建文本消息

 @param content 内容
 @param fromId 发送id
 @param toId 接收id
 @param mtype 消息类型
 @param conversationId 会话id
 @return BMXMessageObject
 */
 BMXMessageObject *messageObject = [[BMXMessageObject alloc] initWithBMXMessageText:message
                                                              fromId:[self.account.usedId longLongValue]
                                                                toId:toId
                                                                type:self.messageType
                                                      conversationId:conversationId];
```

### 图片消息

```
	/**
  创建图片消息对象

  @param aData 二进制数据
  @param aThumbnailData 缩略图
  @param imageSize 图片大小
  @param conversationId 会话IDID
  @return BMXImageAttachment
  */
 BMXImageAttachment *imageAttachment = [[BMXImageAttachment alloc] initWithData:imageData thumbnailData:thumImageData imageSize:image.size conversationId:[NSString stringWithFormat:@"%lld",self.conversationId]];
```

### 文件消息

```
 BMXFileAttachment *fileAttachment = [[BMXFileAttachment alloc] initWithData:dic[@"data"] displayName:dic[@"displayName"] fileLength:integer conversationId:[NSString stringWithFormat:@"%lld",self.conversationId]];
 BMXMessageObject *messageObject = [[BMXMessageObject alloc] initWithBMXMessageAttachment: fileAttachment
                                                                    fromId:[self.account.usedId longLongValue]
                                                                      toId:toId type:self.messageType
                                                            conversationId:conversationId];
```

### 位置消息

```
 BMXLocationAttachment *locationment = [[BMXLocationAttachment alloc] initWithLatitude:latitude longitude:longitude address:address]
 BMXMessageObject *messageObject = [[BMXMessageObject alloc] initWithBMXMessageAttachment: locationAttachment
                                                                    fromId:[self.account.usedId longLongValue]
                                                                      toId:toId type:self.messageType
                                                            conversationId:conversationId];
```

### 语音消息

```
 BMXVoiceAttachment *vocieAttachment = [[BMXVoiceAttachment alloc] initWithPath:voicePath displayName:@"voice" duration:duartion];
 BMXMessageObject *messageObject = [[BMXMessageObject alloc] initWithBMXMessageAttachment: vocieAttachment
                                                                    fromId:[self.account.usedId longLongValue]
                                                                      toId:toId type:self.messageType
                                                            conversationId:conversationId];
```

### 消息操作

消息实体构建完成后，通过 BMXClient的单例，ChatService类，调用 -sendMessage: 方法，将构建好的消息实体传入，即可实现消息发送

* 发送

  ```
   /**
    发送消息，消息状态变化会通过listener通知
   **/
   [[[BMXClient sharedClient] chatService] sendMessage:messageObject completion:^(BMXMessageObject *message, BMXError *error) {
     MAXLog(@"发送消息%@", error);
   }];
  ```
* 转发

  ```
   /**
   简单转发消息，用户应当通过BMXMessagseObject,initWithForwardMessage先创建转发消息
   **/
   BMXMessageObject *m = [[BMXMessageObject alloc] initWithForwardMessage:self.currentMessage fromId:[self.account.usedId longLongValue] toId:roster.rosterId type:BMXMessageTypeSingle conversationId:roster.rosterId];
   [[[BMXClient sharedClient] chatService] forwardMessage:m];
  ```
* 重发

  ```
    /**
   重新发送消息，消息状态变化会通过listener通知
   **/
   [[[BMXClient sharedClient] chatService] resendMessage: self.messageModel.messageObjc completion:^(BMXMessageObject *message, BMXError *error) {
   }];
  ```
* 撤回

  ```
   /**
   撤回消息，消息状态变化会通过listener通知
   **/
   [[[BMXClient sharedClient] chatService] recallMessage:message completion:nil];
  ```
* 下载消息附件

  ```
     /**
    * 下载附件，下载状态变化和进度通过listener通知
    **/
     [[[BMXClient sharedClient] chatService] downloadAttachment:message downloadProgress:nil completion:nil];
  ```

## 消息接收监听

注册消息回调

```
	/**
    * 添加聊天监听者
    **/
	- (void)addChatListener:(id<BMXChatServiceProtocol>)listener;

	/**
	 * 移除聊天监听者
	**/
	- (void)removeChatListener:(id<BMXChatServiceProtocol>)listener;
```

* 接收到消息通知

  ```
    /**
    * 收到消息
    **/
    - (void)receivedMessages:(NSArray<BMXMessageObject*> *)messages {

    	  if (message.contentType == BMXContentTypeText) {
        			// 收到文本消息，UI等处理

          } else if (message.contentType == BMXContentTypeImage) {
              	// 收到图片消息

          } else if (message.contentType == BMXContentTypeVoice) {
    				// 收到语音消息

          } else if (message.contentType == BMXContentTypeLocation) {
           		// 收到位置消息

          } else if (message.contentType == BMXContentTypeFile) {
              	// 收到文件消息

          }
    }
  ```
* 消息发送后状态回调通知

  ```
    //  消息状态发生变化
    - (void)messageStatusChanged:(BMXMessageObject *)message
                   error:(BMXError *)error {

       if (message && !error) {
        switch ( message.deliverystatus) {
            case BMXDeliveryStatusNew:
                messageModel.status = MessageDeliveryState_Pending;
                break;
            case BMXDeliveryStatusDelivering:
                messageModel.status = MessageDeliveryState_Delivering;
                break;
            case BMXDeliveryStatusDeliveried:
                messageModel.status = MessageDeliveryState_Delivered;
                break;
            case BMXDeliveryStatusFailed:
                messageModel.status = MessageDeliveryState_Failure;
                break;
            case BMXDeliveryStatusRecalled:
                messageModel.status = MessageDeliveryState_Pending;
                break;

            default:
                break;
        }
        [messagecell layoutSubviews];

    	}

    }
  ```
* 附件消息发送状态回调

  ```
    - (void)messageAttachmentUploadProgressChanged:(BMXMessageObject *)message percent:(int)percent {
    // percent为上传进度百分比

    }
  ```
* 消息提醒设置 通过 BMXClient的单例，UserService类，以下方法可以设置消息推送提醒

  ```
    /**
    设置是否允许推送

    @param enablePushStatus BOOL
    */
    - (void)setEnablePushStatus:(BOOL)enablePushStatus
             completion:(void(^)(BMXError *error))aCompletionBlock;

    /**
      设置是否推送详情

      @param enablePushDetail BOOL
     */
  ```

p - (void)setEnablePushDetail:(BOOL)enablePushDetail completion:(void(^)(BMXError \*error))aCompletionBlock;

* 附件消息下载状态变化

  ```
    /**
    * 附件下载状态发生变化
    **/
    - (void)messageAttachmentStatusDidChanged:(BMXMessageObject *)message
                      error:(BMXError*)error
                    percent:(int)percent;
  ```

## 功能进阶

BMXMessageObject实体中，提供可扩展属性(extensionJson 和 configJson) extensionJson 为开发使用的扩展字段，例如编辑状态。 configJson 为SDK自用的扩展字段，例如mention功能，push功能

* 群组@功能

  群组中支持 @ 功能，满足您 @ 指定用户或 @ 所有人的需求，开发者在BMXMessageObject中通过设置configJson 来实现群主@功能，已经@成员后的会下发推送通知
* 消息正在输入状态

  ```
    // 可以使用extensionJson，来扩展正在编辑状态消息，（json格式，可以扩展多种自定义功能）
    @property (nonatomic, copy) NSString *extensionJson;
  ```
* 消息阅读回执

  ```
    //全部消息是否已读
    @property (nonatomic,assign) BOOL isRead;
    //接受消息是否发送已读回执
    @property (nonatomic,assign) BOOL isReadAcked;
    //接受消息是否发送已送达
    @property (nonatomic, assign) BOOL isDeliveryAcked;
  ```
* 多端阅读消息数同步

  BMXConversation 实体提供消息未读数量和会话中所有消息数量 /\*\* 未读消息数量 \*/ @property (nonatomic,assign, readonly) NSInteger unreadNumber;

  ```
    /**
     会话中所有消息数量
    */
    @property (nonatomic,assign, readonly) NSInteger messageCount;
  ```
* 消息搜索

  根据关键字搜索指定消息内容

  ```
    /**
     搜索消息

     @param keywords 关键字
     @param refTime 时间
     @param size 分页
     @param directionType 消息顺序
     @param aCompletionBlock 搜索结果
    */

    [[[BMXClient sharedClient] chatService] searchMessages:@"哈哈" refTime:0 size:100 directionType:BMXMessageDirectionUp completion:^(NSArray *array, BMXError *error) {

    }];
  ```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs-old.maximtop.com/quick-start/floo-ios-quick-start.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
