Skip to main content

Messaging – Fetch Past Messages

Overview

Fetching past messages allows a Swift application to retrieve historical messages that were previously published to a topic. This API is used when real-time delivery alone is not sufficient, such as during application startup, recovery after downtime, or when running analytics over earlier events.

Unlike subscriptions, fetching message history does not create a live stream. It is a point-in-time query against messages that are still retained by RelayX. The result is a finite list of messages that match the query criteria at the time the request is executed.

This API interacts with RelayX's message storage layer. It is constrained by message retention limits, topic validity rules, and connection state. It is designed to complement subscriptions, not replace them.


How This Works

When history() is called, the Swift SDK sends a request to the RelayX backend to retrieve messages that match the provided topic and time range.

RelayX immediately evaluates the request against:

  • The topic or wildcard pattern
  • The specified start and end timestamps
  • The current retention window of the account

If the client is connected, RelayX returns all matching messages that still exist. If the client is disconnected or reconnecting, the SDK returns an empty list. No retry or buffering is performed for history requests.

A history request is considered successful if it returns a list. An empty list does not necessarily indicate an error. It may mean there were no messages, the messages have expired, or the client was not connected at the time of the request.


API Usage

Method

func history(topic: String, start: Date, end: Date?, limit: Int?) async throws -> [[String: Any]]

Note: history() is an async function and must be called with await from an async context.

Parameters

  • topic (String, required) A valid topic name or wildcard pattern. Topic rules and restrictions apply.

  • start (Date, required) The beginning of the time range for fetching messages, specified as a Date object.

  • end (Date?, optional)

    • The end of the time range for fetching messages, specified as a Date object.
    • This must be later than start.
    • If not specified, history from start to now will be returned.
  • limit (Int?, optional)

    • The maximum number of messages to fetch.
    • If not specified, all matching messages will be returned.

Return Value

  • Returns an array of dictionaries [[String: Any]] containing message data
  • Returns an empty array if:
    • No messages match
    • Messages have expired
    • The client is disconnected or reconnecting

Message Dictionary Structure

// Each dictionary in the returned array has the following structure:
[
"id": String, // Message ID
"topic": String, // Topic the message was published to
"message": Any, // The actual message payload (String, Number, or Dictionary)
"timestamp": Int // Timestamp when message was created (milliseconds)
]

Code Example

import Realtime
import Foundation

Task {
do {
// Initialize and connect to Realtime
let realtime = try Realtime(apiKey: "<API KEY>", secret: "<SECRET KEY>")
try realtime.prepare(staging: false, opts: ["debug": true])
try await realtime.connect()

let now = Date()

// Set start time to 4 days ago
let fourDaysAgo = Calendar.current.date(byAdding: .day, value: -4, to: now)!

// Set end time to 2 days ago
let twoDaysAgo = Calendar.current.date(byAdding: .day, value: -2, to: now)!

// Fetch history with start and end time
let history = try await realtime.history(
topic: "chat.room1",
start: fourDaysAgo,
end: twoDaysAgo,
limit: nil
)

// OR

// This will return messages from fourDaysAgo to now
let historyToNow = try await realtime.history(
topic: "chat.room1",
start: fourDaysAgo,
end: nil,
limit: nil
)

// Process the results
for message in history {
print("Message ID: \(message["id"] ?? "N/A")")
print("Topic: \(message["topic"] ?? "N/A")")
print("Content: \(message["message"] ?? "N/A")")
print("Timestamp: \(message["timestamp"] ?? "N/A")")
print("---")
}

} catch {
print("Error: \(error)")
}
}

Structure of the returned array:

// Array of message dictionaries
[
[
"id": "<MESSAGE UUID>",
"topic": "<TOPIC MATCHING TOPIC / WILDCARD TOPIC>",
"message": <Actual message as String, Number, or Dictionary>,
"timestamp": <Timestamp in milliseconds>
],
// ... more messages
]

Failure & Edge Cases

Disconnected Client

If history() is called while the client is disconnected or reconnecting, the SDK returns an empty list. No exception is thrown.

Invalid Topics

An error is thrown if:

  • The topic name is invalid
  • A reserved system topic is used

Invalid Time Range

An error is thrown if:

  • end is earlier than or equal to start

Expired Messages

Messages that exceed their TTL are permanently deleted. Fetching outside the retention window always returns an empty list.


Common Mistakes

  1. Assuming history waits for reconnection
    History requests do not retry. Call history only after a successful connection.

  2. Treating empty results as an error An empty list is a valid response in many scenarios.

  3. Using history instead of subscriptions
    History does not stream new messages. Use subscriptions for live data.

  4. Requesting data outside retention limits
    Messages older than the plan’s retention window cannot be fetched.


Notes & Limitations

  • History requests are bounded by message retention and plan limits.
  • Large time ranges may return large lists and impact memory usage.
  • History does not participate in reconnect replay logic.
  • Timestamps are specified using Swift's Date objects for easier time manipulation.
  • The returned timestamp in message dictionaries is in milliseconds since Unix epoch.
Need Help?

Join our Discord server, post your concern & someone from our team will help you out ✌️