# Message history (Beta) (/en/realtime-media/rtm/build/send-and-receive-messages/message-history/macos)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

The Signaling message history feature allows you to store messages when publishing them to a channel. For example, if a user joins a channel midway through a conversation, you can retrieve messages that were published before the user joined. You can configure the message retention period for each project, ranging from 1 day to permanent storage.

When a message is published, it is stored using the channel name and the message timestamp. You can use this information to retrieve historical messages.

<CalloutContainer type="info">
  <CalloutDescription>
    The historical message feature is currently available for User Channels and Message Channels, but not for Stream Channels.
  </CalloutDescription>
</CalloutContainer>

## Prerequisites

Before implementing this feature, ensure that you have:

* Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../index) page.
* Enabled the message history feature by contacting [support@agora.io](mailto\:support@agora.io). You can request a storage duration of 1, 7, 30, 90, or 365 days, or opt for permanent storage.

  <CalloutContainer type="info">
    <CalloutDescription>
      Changes to storage duration only affect future messages.
    </CalloutDescription>
  </CalloutContainer>

## Implement message history

This section shows how to store and retrieve messages from history.

### Store messages

To store a message on the server, set `setStoreInHistory` to `true` in the `publish()` method. The following example shows the minimum code to store a message published to a message channel:

<CodeBlockTabs defaultValue="Swift">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="Swift">
      Swift
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="Objc">
      Objc
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="Swift">
    ```swift
    let message = "Hello Agora!"
    let channel = "your_channel"

    let publishOption = AgoraRtmPublishOptions()
    publishOption.storeInHistory = true;

    rtmKit.publish(channelName: channel, message: message, option: publishOption, completion: { res, error in
        if error != nil {
            print("\(error?.operation) failed! error reason is \(error?.reason)")
        } else {
            print("success")
        }
    })
    ```
  </CodeBlockTab>

  <CodeBlockTab value="Objc">
    ```objc
    NSString* message = @"Hello Agora!";
    NSString* channel = @"your_channel";

    AgoraRtmPublishOptions* publish_option = [[AgoraRtmPublishOptions alloc] init];
    publish_option.storeInHistory = true;

    [_kit publish:channel message:message option:publish_option completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
        if (errorInfo == nil) {
            NSLog(@"publish success!!");
        } else {
            NSLog(@"publish failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
        }
    }];
    ```
  </CodeBlockTab>
</CodeBlockTabs>

To store a message in the history of a user channel, use the same parameter:

<CodeBlockTabs defaultValue="Swift">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="Swift">
      Swift
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="Objc">
      Objc
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="Swift">
    ```swift
    let message = "Hello Agora!"
    let user = "Tony"

    let publishOption = AgoraRtmPublishOptions()
    publishOption.channelType = .user
    publishOption.storeInHistory = true;

    rtmKit.publish(channelName: user, message: message, option: publishOption, completion: { res, error in
        if error != nil {
            print("\(error?.operation) failed! error reason is \(error?.reason)")
        } else {
            print("success")
        }
    })
    ```
  </CodeBlockTab>

  <CodeBlockTab value="Objc">
    ```objc
    NSString* message = @"Hello Agora!";
    NSString* user = @"Tony";

    AgoraRtmPublishOptions* publish_option = [[AgoraRtmPublishOptions alloc] init];
    publish_option.channelType = AgoraRtmChannelTypeUser;
    publish_option.storeInHistory = true;

    [_kit publish:user message:message option:publish_option completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
        if (errorInfo == nil) {
            NSLog(@"publish success!!");
        } else {
            NSLog(@"publish failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
        }
    }];
    ```
  </CodeBlockTab>
</CodeBlockTabs>

### Retrieve messages

The SDK provides the `getMessages()` method to retrieve up to 100 history messages at a time. You can filter messages by setting the `start` and `end` parameters to define a time range. To retrieve only the most recent messages, such as those sent between the last offline time and now, set only the `end` parameter.

The following example retrieves the latest 50 messages sent before a specific time:

<CodeBlockTabs defaultValue="Swift">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="Swift">
      Swift
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="Objc">
      Objc
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="Swift">
    ```swift
    var option = AgoraRtmGetHistoryMessagesOptions()
     option.messageCount = 50
     option.end = 1688978391800

    rtmKit.getHistory()?.getMessages(channelName: "channel_name", channelType: .message, options: option, completion: { response, error in
        if response != nil {
            print("total message count is: \(response!.messageList.count)")
                for msg in response!.messageList {
                    print("publisher: \(msg.publisher)message content: \(msg.message.stringData!)time stamp: \(msg.timestamp)")
                }
                print("new start: \(response!.newStart)")
        } else {
            print("get history message failed")
        }
    })
    ```
  </CodeBlockTab>

  <CodeBlockTab value="Objc">
    ```objc
    AgoraRtmGetHistoryMessagesOptions *option = [[AgoraRtmGetHistoryMessagesOptions alloc] init];
    option.messageCount = 50;
    option.end = 1688978391800;

    [_kit getMessages:@"channel_name"
                channelType:AgoraRtmChannelTypeMessage
                    options:option
                 completion:^(AgoraRtmGetHistoryMessagesResponse *response, NSError *error) {
        if (response != nil) {
            NSLog(@"total message count is: %lu", (unsigned long)response.messageList.count);

            for (AgoraRtmMessage *msg in response.messageList) {
                NSLog(@"publisher: %@message content: %@time stamp: %lld", msg.publisher, msg.message.stringData, msg.timestamp);
            }

            NSLog(@"new start: %@", response.newStart);
        } else {
            NSLog(@"get history message failed: %@", error.localizedDescription);
        }
    }];
    ```
  </CodeBlockTab>
</CodeBlockTabs>

The `response` includes a `newStart` field, which indicates whether there are unread messages in the history:

* If `newStart` is `0`, all messages have been retrieved.
* If `newStart` is not `0`, use this value as the new `start` parameter in the next `getMessages()` call to continue retrieving messages.

The following use-cases show how to specify the `start` and `end` parameters to retrieve messages in a specific range:

#### Retrieve before a start timestamp

| Parameter | Behavior                                                                                |
| --------- | --------------------------------------------------------------------------------------- |
| `start`   | Returns messages sent before the specified timestamp. The timestamp itself is excluded. |

```text
Timeline:
[Older messages] ------------ start timestamp ------------ [Newer messages]
[             ←-- n messages]
```

#### Retrieve messages up to an end timestamp

| Parameter | Behavior                                                                             |
| --------- | ------------------------------------------------------------------------------------ |
| `end`     | Returns the n most recent messages sent up to and including the specified timestamp. |

```text
Timeline:
[Older messages] ------------ end timestamp ------------ [Newer messages]
                                       [  ←-- n messages]
```

#### Retrieve messages between two timestamps

| Parameter | Behavior                                                  |
| --------- | --------------------------------------------------------- |
| `start`   | Returns messages sent before this timestamp (excluded).   |
| `end`     | Returns messages sent up to and including this timestamp. |

```text
Timeline:
[Older messages] ---- end timestamp ------------ start timestamp ---- [Newer messages]
                               [ ←-- n messages]
```

    
  
      
  
      
  
