---
source_url: https://www.pubnub.com/docs/sdks/cocoa-swift/api-reference/storage-and-playback
title: Storage and Playback API for Cocoa Swift SDK
updated_at: 2026-06-18T11:27:24.657Z
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Storage and Playback API for Cocoa Swift SDK

This SDK has been replaced by a new PubNub Swift SDK written purely in Swift. Check out [the Swift SDK page](https://www.pubnub.com/docs/sdks/swift)

Message Persistence gives you real-time access to the history of messages published to PubNub. Each message is timestamped to the nearest 10 nanoseconds and stored across multiple availability zones in several geographic locations. You can encrypt stored messages with AES-256 so they are not readable on PubNub’s network. For details, see [Message Persistence](https://www.pubnub.com/docs/general/storage).

You control how long messages are stored through your account’s retention policy. Options include: 1 day, 7 days, 30 days, 3 months, 6 months, 1 year, or Unlimited.

You can retrieve the following:

* Messages
* Message reactions
* Files (using the File Sharing API)

## History

:::warning Requires Message Persistence
Enable Message Persistence for your key in the [Admin Portal](https://admin.pubnub.com/). See how to [enable add-on features](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-).
:::

This function fetches historical messages of a channel.

You can control how messages are returned and in what order:

* Search for messages starting on the newest end of the timeline (default behavior - `reverse` = `false`).
* Search for messages from the oldest end of the timeline by setting `reverse` to `true`.
* Page through results by providing a `startDate` or `endDate` [timetoken](https://www.pubnub.com/docs/sdks/cocoa-swift/api-reference/misc#time).
* Retrieve a slice of the timeline by providing both a `startDate` and `endDate` timetoken.
* Limit the number of messages using the `limit` parameter.

:::tip Start & End parameter usage clarity
If you specify only `startDate` (without `endDate`), you receive messages older than and up to that `startDate` timetoken. If you specify only `endDate` (without `startDate`), you receive messages that match that `endDate` timetoken and newer. If you specify both `startDate` and `endDate`, you receive messages between those timetokens (inclusive of `endDate`). You still receive up to 100 messages even if more match. Make iterative calls to history, adjusting `startDate`, to page through the full result set.
:::

### Method(s)

To run `History` you can use the following method(s) in the Swift SDK:

#### History with closure

```swift
open func historyForChannel(
    _ channel: String,
    withCompletion closure: PubNub.PNHistoryCompletionBlock
)
```

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| channel | String | Yes |  | `Channel` name to retrieve the History information. |
| closure | PNHistoryCompletionBlock | Yes |  | The completion `closure` has two arguments: **result** - in case of successful processing (data field will contain results of history request operation); **status** - in case if error occurred during request processing (errorData contains error information). |

#### History with dates and closure

```swift
open func historyForChannel(
    _ channel: String,
    start startDate: NSNumber?,
    end endDate: NSNumber?,
    withCompletion closure: PubNub.PNHistoryCompletionBlock
)
```

| Parameter | Description |
| --- | --- |
| `channel` *Type: String | `Channel` name to retrieve the History information. |
| `startDate`Type: NSNumber | Timetoken delimiting the start of time slice (exclusive) to pull messages from. |
| `endDate`Type: NSNumber | Timetoken delimiting the end of time slice (inclusive) to pull messages from. |
| `closure` *Type: PNHistoryCompletionBlock | The completion `closure` has two arguments: **result** - in case of successful processing (data field will contain results of history request operation); **status** - in case if error occurred during request processing (errorData contains error information). |

#### History with dates, limit, and closure

```swift
open func historyForChannel(
    _ channel: String,
    start startDate: NSNumber?,
    end endDate: NSNumber?,
    limit: UInt,
    withCompletion closure: PubNub.PNHistoryCompletionBlock
)
```

| Parameter | Description |
| --- | --- |
| `channel` *Type: String | `Channel` name to retrieve the History information. |
| `startDate`Type: NSNumber | Timetoken delimiting the start of time slice (exclusive) to pull messages from. |
| `endDate`Type: NSNumber | Timetoken delimiting the end of time slice (inclusive) to pull messages from. |
| `limit` *Type: UInt | Specifies the number of historical messages to return. default/maximum is 100. |
| `closure` *Type: PNHistoryCompletionBlock | The completion `closure` has two arguments: **result** - in case of successful processing (data field will contain results of history request operation); **status** - in case if error occurred during request processing (errorData contains error information). |

#### History with dates, timetoken, and closure

```swift
open func historyForChannel(
    _ channel: String,
    start startDate: NSNumber?,
    end endDate: NSNumber?,
    includeTimeToken shouldIncludeTimeToken: Bool,
    withCompletion closure: PubNub.PNHistoryCompletionBlock
)
```

| Parameter | Description |
| --- | --- |
| `channel` *Type: String | `Channel` name to retrieve the History information. |
| `startDate`Type: NSNumber | Timetoken delimiting the start of time slice (exclusive) to pull messages from. |
| `endDate`Type: NSNumber | Timetoken delimiting the end of time slice (inclusive) to pull messages from. |
| `shouldIncludeTimeToken` *Type: Bool | If true the `message` post timestamps will be included in the history response. |
| `closure` *Type: PNHistoryCompletionBlock | The completion `closure` has two arguments: **result** - in case of successful processing (data field will contain results of history request operation); **status** - in case if error occurred during request processing (errorData contains error information). |

#### History with dates, limit, timetoken, and closure

```swift
open func historyForChannel(
    _ channel: String,
    start startDate: NSNumber?,
    end endDate: NSNumber?,
    limit: UInt,
    includeTimeToken shouldIncludeTimeToken: Bool,
    withCompletion closure: PubNub.PNHistoryCompletionBlock
)
```

| Parameter | Description |
| --- | --- |
| `channel` *Type: String | `Channel` name to retrieve the History information. |
| `startDate`Type: NSNumber | Timetoken delimiting the start of time slice (exclusive) to pull messages from. |
| `endDate`Type: NSNumber | Timetoken delimiting the end of time slice (inclusive) to pull messages from. |
| `limit` *Type: UInt | Specifies the number of historical messages to return. default/maximum is 100. |
| `shouldIncludeTimeToken` *Type: Bool | If `true` the `message` post timestamps will be included in the history response. |
| `closure` *Type: PNHistoryCompletionBlock | The completion `closure` has two arguments: **result** - in case of successful processing (data field will contain results of history request operation); **status** - in case if error occurred during request processing (errorData contains error information). |

#### History with dates, limit, and closure, in reverse order

```swift
open func historyForChannel(
    _ channel: String,
    start startDate: NSNumber?,
    end endDate: NSNumber?,
    limit: UInt,
    reverse shouldReverseOrder: Bool,
    withCompletion closure: PubNub.PNHistoryCompletionBlock
)
```

| Parameter | Description |
| --- | --- |
| `channel` *Type: String | `Channel` name to retrieve the History information. |
| `startDate`Type: NSNumber | Timetoken delimiting the start of time slice (exclusive) to pull messages from. |
| `endDate`Type: NSNumber | Timetoken delimiting the end of time slice (inclusive) to pull messages from. |
| `limit` *Type: UInt | Specifies the number of historical messages to return. Default/maximum is 100. |
| `shouldReverseOrder` *Type: Bool | Setting to `true` will traverse the time line in reverse starting with the oldest `message` first. Default is false. If both `start` and `end` arguments are provided, reverse is ignored and messages are returned starting with the newest `message`. |
| `closure` *Type: PNHistoryCompletionBlock | The completion `closure` has two arguments: **result** - in case of successful processing (data field will contain results of history request operation); **status** - in case if error occurred during request processing (errorData contains error information). |

#### History with dates, limit, timetoken, and closure, in reverse order

```swift
open func historyForChannel(
    _ channel: String,
    start startDate: NSNumber?,
    end endDate: NSNumber?,
    limit: UInt,
    reverse shouldReverseOrder: Bool,
    includeTimeToken shouldIncludeTimeToken: Bool,
    withCompletion closure: PubNub.PNHistoryCompletionBlock
)
```

| Parameter | Description |
| --- | --- |
| `channel` *Type: String | `Channel` name to retrieve the History information. |
| `startDate`Type: NSNumber | `Timetoken` delimiting the start of time slice (exclusive) to pull messages from. |
| `endDate`Type: NSNumber | `Timetoken` delimiting the end of time slice (inclusive) to pull messages from. |
| `limit` *Type: UInt | Specifies the number of historical messages to return. default/maximum is 100. |
| `shouldReverseOrder` *Type: Bool | Setting to `true` will traverse the time line in reverse starting with the oldest `message` first. Default is `false`. If both start and end arguments are provided, reverse is ignored and messages are returned starting with the newest `message`. |
| `shouldIncludeTimeToken` *Type: Bool | If true the `message` post timestamps will be included in the history response. |
| `closure` *Type: PNHistoryCompletionBlock | The completion `closure` has two arguments: **result** - in case of successful processing (data field will contain results of history request operation); **status** - in case if error occurred during request processing (errorData contains error information). |

:::tip Using the reverse parameter
Messages are always returned sorted in ascending time direction from history regardless of `reverse`. The `reverse` direction matters when you have more than 100 (or `limit`, if it's set) messages in the time interval, in which case `reverse` determines the end of the time interval from which it should start retrieving the messages.
:::

### Sample code

Retrieve the last 100 messages on a channel:

```swift
self.client.historyForChannel("history_channel", withCompletion: { (result, status) in

    if status == nil {

        /**
         Handle downloaded history using:
            result.data.start - oldest message time stamp in response
            result.data.end - newest message time stamp in response
            result.data.messages - list of messages
         */
    }
    else {

        /**
         Handle message history download error. Check 'category' property
         to find out possible reason because of which request did fail.
         Review 'errorData' property (which has PNErrorData data type) of status
         object to get additional information about issue.

         Request can be resent using: status.retry()
         */
    }
})
```

### Response

The response object which is returned by the client when the history API is used:

```swift
open class PNHistoryData : PNServiceData {

    // Channel history messages.
    open var messages: [Any] { get }
    // History time frame start time.
    open var start: NSNumber { get }
    // History time frame end time.
    open var end: NSNumber { get }
}

open class PNHistoryResult : PNResult {

    // Stores reference on channel history request processing information.
    open var data: PNHistoryData { get }
}
```

### Other examples

#### Use historyForChannel to retrieve the three oldest messages by retrieving from the time line in reverse

```swift
self.client.historyForChannel("my_channel", start: nil, end: nil, limit: 3,
                                reverse: true, withCompletion: { (result, status) in

    if status == nil {

        /**
            Handle downloaded history using:
            result.data.start - oldest message time stamp in response
            result.data.end - newest message time stamp in response
            result.data.messages - list of messages
            */
    }
    else {

        /**
            Handle message history download error. Check 'category' property
            to find out possible reason because of which request did fail.
            Review 'errorData' property (which has PNErrorData data type) of status
            object to get additional information about issue.

            Request can be resent using: status.retry()
            */
    }
})
```

##### Response

```json
[
    ["Pub1","Pub2","Pub3"],
    13406746729185766,
    13406746780720711
]
```

#### Use historyForChannel to retrieve messages newer than a given timetoken by paging from oldest message to newest message starting at a single point in time (exclusive)

```swift
let start = NSNumber(value: (13406746780720711 as CUnsignedLongLong))
self.client.historyForChannel("my_channel", start: start, end: nil, limit: 100,
                                reverse: true, withCompletion: { (result, status) in

    if status == nil {

        /**
            Handle downloaded history using:
            result.data.start - oldest message time stamp in response
            result.data.end - newest message time stamp in response
            result.data.messages - list of messages
            */
    }
    else {

        /**
            Handle message history download error. Check 'category' property
            to find out possible reason because of which request did fail.
            Review 'errorData' property (which has PNErrorData data type) of status
            object to get additional information about issue.

            Request can be resent using: status.retry()
            */
    }
})
```

##### Response

```json
[
    ["Pub3","Pub4","Pub5"],
    13406746780720711,
    13406746845892666
]
```

#### Use historyForChannel to retrieve messages until a given timetoken by paging from newest message to oldest message until a specific end point in time (inclusive)

```swift
let end = NSNumber(value: (13406746780720711 as CUnsignedLongLong))
self.client.historyForChannel("my_channel", start: nil, end: end, limit: 100, reverse: true,
                                withCompletion: { (result, status) in

    if status == nil {

        /**
            Handle downloaded history using:
            result.data.start - oldest message time stamp in response
            result.data.end - newest message time stamp in response
            result.data.messages - list of messages
            */
    }
    else {

        /**
            Handle message history download error. Check 'category' property
            to find out possible reason because of which request did fail.
            Review 'errorData' property (which has PNErrorData data type) of status
            object to get additional information about issue.

            Request can be resent using: status.retry()
            */
    }
})
```

##### Response

```json
[
    ["Pub3","Pub4","Pub5"],
    13406746780720711,
    13406746845892666
]
```

#### History paging example

:::note Usage
You can call the method by passing 0 or a valid **timetoken** as the argument.
:::

```swift
// Pull out all messages newer than message sent at 14395051270438477.
let date = NSNumber(value: (14395051270438477 as CUnsignedLongLong));
self.historyNewerThan(date, onChannel: "history_channel", withCompletion:  { (messages) in

    print("Messages from history: \(messages)")
})

func historyNewerThan(_ date: NSNumber, onChannel channel: String,
                        withCompletion closure: @escaping (Array<Any>) -> Void) {

    var msgs: Array<Any> = []
    self.historyNewerThan(date, onChannel: channel, withProgress: { (messages) in

        msgs.append(contentsOf: messages)
        if messages.count < 100 { closure(msgs) }
    })
}

private func historyNewerThan(_ date: NSNumber, onChannel channel: String,
                                withProgress closure: @escaping (Array<Any>) -> Void) {

    self.client?.historyForChannel(channel, start: date, end: nil, limit: 100,
                                    reverse: false, withCompletion: { (result, status) in

        if status == nil {

            closure((result?.data.messages)!)
            if result?.data.messages.count == 100 {

                self.historyNewerThan((result?.data.start)!, onChannel: channel,
                                        withProgress: closure)
            }
        }
        else {

            /**
                Handle message history download error. Check the 'category' property
                to find out why the request failed.
                Review the 'errorData' property (of type PNErrorData) of the status
                object to get additional information about the issue.

                Request can be resent using: [status retry];
            */
        }
    })
}
```

## History (builder pattern)

This function fetches historical messages of a channel.

This method uses the builder pattern; you can omit any optional arguments.

### Method(s)

To get channel history using the Builder pattern, use the following method(s) in the Swift SDK:

```swift
history().channels([String])
    .limit(UInt)
    .performWithCompletion(PubNub.PNHistoryCompletionBlock)
```

| Parameter | Description |
| --- | --- |
| `channels` *Type: [String] | List of `channels` for which history should be returned. |
| `limit`Type: UInt | Maximum number of messages which should be returned for each channel. Default and maximum value is 100 for a single channel, 25 for multiple channels. |
| `completion` *Type: PNHistoryCompletionBlock | History pull processing completion block which pass two arguments: result - in case of successful request processing data field will contain results of history request operation; status - in case of error occurred during request processing. |

:::tip Using the reverse parameter
Messages are always returned sorted in ascending time direction from history regardless of `reverse`. The `reverse` direction matters when you have more than 100 (or `limit`, if it's set) messages in the time interval, in which case `reverse` determines the end of the time interval from which it should start retrieving the messages.
:::

### Sample code

```swift
self.client.history().channels(["my_channel"]).limit(15).performWithCompletion({ (result, status) in

    if status == nil {

        /**
         Handle downloaded history using:
         result.data.channels - dictionary with channels' history. Each key is channel name and value is
         list of fetched messages.
         */
    }
    else {

        /**
         Handle message history download error. Check 'category' property
         to find out possible reason because of which request did fail.
         Review 'errorData' property (which has PNErrorData data type) of status
         object to get additional information about issue.

         Request can be resent using: [status retry]
         */
    }
})
```

## Delete messages from history

:::warning Requires Message Persistence
This method requires that Message Persistence is [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/).
:::

Removes the messages from the history of a specific channel.

:::note Delete-From-History
There is a setting to accept delete from history requests for a key, which you must enable by checking the Enable `Delete-From-History` checkbox in the key settings for your key in the Admin Portal.
Requires Initialization with secret key.
:::

### Method(s)

To `Delete Messages from History` you can use the following method(s) in the Swift SDK.

```swift
open func deleteMessagesFromChannel(
    _ channel: String,
    start startDate: NSNumber?,
    end endDate: NSNumber?,
    withCompletion closure: PubNub.PNMessageDeleteCompletionBlock? = nil
)
```

### Sample code

```swift
let startDate = NSNumber(value: (15101397027611671 as CUnsignedLongLong))
let endDate = NSNumber(value: (15101397427611671 as CUnsignedLongLong))
self.client.deleteMessagesFromChannel("channel", start: startDate, end: endDate, withCompletion: { (status) in

    if !status.isError {
        // Messages within specified time frame has been removed.
    } else {
       /**
        * Handle message history download error. Check 'category' property to find out possible
        * issue because of which request did fail.
        *
        * Request can be resent using: status.retry()
        */
    }
})
```

### Other examples

#### Delete specific message from history

To delete a specific message, pass the `publish timetoken` (received from a successful publish) in the `End` parameter and `timetoken +/- 1` in the `Start` parameter. For example, if `15526611838554310` is the `publish timetoken`, pass `15526611838554309` in `Start` and `15526611838554310` in `End` parameters respectively as shown in the following code snippet.

```swift
let startDate = NSNumber(value: (15526611838554309 as CUnsignedLongLong))
let endDate = NSNumber(value: (15526611838554310 as CUnsignedLongLong))
self.client.deleteMessagesFromChannel("channel", start: startDate, end: endDate, withCompletion: { (status) in

    if !status.isError {
        // Messages within specified time frame has been removed.
    } else {
        /**
        * Handle message history download error. Check 'category' property to find out possible
        * issue because of which request did fail.
        *
        * Request can be resent using: status.retry()
        */
    }
})
```

## Delete messages from history (builder pattern)

:::warning Requires Message Persistence
This method requires that Message Persistence is [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/).
:::

Removes the messages from the history of a specific channel.

:::note Delete-From-History
There is a setting to accept delete from history requests for a key, which you must enable by checking the Enable `Delete-From-History` checkbox in the key settings for your key in the Admin Portal.
Requires Initialization with secret key.
:::

### Method(s)

To `Delete Messages from History` you can use the following method(s) in the Swift SDK.

```swift
deleteMessage().channel(String)
    .start(NSNumber?)
    .end(NSNumber?)
    .performWithCompletion(PubNub.PNMessageDeleteCompletionBlock? = nil)
```

### Sample code

```swift
let startDate = NSNumber(value: (15101397027611671 as CUnsignedLongLong))
let endDate = NSNumber(value: (15101397427611671 as CUnsignedLongLong))
self.client.deleteMessage().channel("channel").start(startDate).end(endDate).performWithCompletion({ (status) in

    if !status.isError {
        // Messages within specified time frame has been removed.
    } else {
       /**
        * Handle message history download error. Check 'category' property to find out possible
        * issue because of which request did fail.
        *
        * Request can be resent using: status.retry()
        */
    }
})
```

## Message counts

:::warning Requires Message Persistence
This method requires that Message Persistence is [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/).
:::

Returns the number of messages published on one or more channels since a given time. The `count` returned is the number of messages in history with a `timetoken` value `greater than or equal to` than the passed value in the `timetokens`parameter.

:::note Unlimited message retention
For keys with unlimited message retention enabled, this method considers only messages published in the last 30 days.
:::

### Method(s)

You can use the following method(s) in the Swift SDK:

```swift
messageCounts().channels([String])
    .timetokens([Int])
    .performWithCompletion(PNMessageCountCompletionBlock)
```

| Parameter | Description |
| --- | --- |
| `channels` *Type: [String]Default: n/a | The `channels` to fetch the message count |
| `timetokens` *Type: [Int]Default: n/a | List with single or multiple timetokens, where each timetoken position in correspond to target `channel` location in channel names list. |
| `completion` *Type: PNMessageCountCompletionBlockDefault: n/a | Messages count fetch completion closure which pass two arguments: `result` - in case of successful request processing data field will contain results of message count fetch operation; `status` - in case of error occurred during request processing. |

### Sample code

```swift
let timetoken = NSNumber(value: (15501015683744028 as CUnsignedLongLong))
self.client.messageCounts().channels(["unread-channel-1", "unread-channel-2"])
    .timetokens([timetoken])
    .performWithCompletion({ (result, status) in

        if !status.isError {
            // Client state retrieved number of messages for channels.
        } else {
            /**
             Handle client state modification error. Check 'category' property
             to find out possible reason because of which request did fail.
             Review 'errorData' property (which has PNErrorData data type) of status
             object to get additional information about issue.

             Request can be resent using: status.retry()
            */
        }
    });
```

### Returns

:::note Channels count
`Channels` without messages have a count of 0. `Channels` with 10,000 messages or more have a count of 10000.
:::

```c
open class PNMessageCountData : PNServiceData {

    // Dictionary where each key is name of channel and value is number of messages in it.
    var channels: [String:Int] { get }
}

open class PNMessageCountResult : PNResult {

    // Message count request processing information.
    var data: PNMessageCountData { get }
}
```

### Other examples

#### Retrieve count of messages using different timetokens for each channel

```swift
let timetoken = NSNumber(value: (15501015683744028 as CUnsignedLongLong))
let timetoken2 = NSNumber(value: (15501015683744130 as CUnsignedLongLong))
self.client.messageCounts().channels(["unread-channel-1", "unread-channel-2"])
    .timetokens([timetoken, timetoken2])
    .performWithCompletion({ (result, status) in

        if !status.isError {
            // Client state retrieved number of messages for channels.
        } else {
            /**
                Handle client state modification error. Check 'category' property
                to find out possible reason because of which request did fail.
                Review 'errorData' property (which has PNErrorData data type) of status
                object to get additional information about issue.

                Request can be resent using: status.retry()
            */
        }
    });
```

## Terms in this document

* **Message** - A unit of data transmitted between clients or between a client and a server in PubNub, containing information such as text, binary data, or structured data formats like JSON. Messages are sent over channels and can be tracked for delivery and read status.
* **PubNub** - PubNub is a real-time messaging platform that provides APIs and SDKs for building scalable applications. It handles the complex infrastructure of real-time communication, including: Message delivery and persistence, Presence detection, Access control, Push notifications, File sharing, Serverless processing with Functions and Events & Actions, Analytics and monitoring with BizOps Workspace, AI-powered insights with Illuminate.
