Authentication & Connection
Overview
This page explains how authentication and connection work in the RelayX Swift SDK.
RelayX uses API key and secret–based authentication. Both values are required for a client to connect to the Relay Network and perform any operation such as publishing messages or subscribing to topics.
Connection management is handled entirely by the SDK. The SDK establishes the connection, monitors its health, retries on failures, and recovers messages automatically. Application code is not expected to manage sockets, retries, or resubscription logic manually.
This page focuses only on how authentication and connection behave in Swift, and what you should expect at runtime.
How This Works
Authentication
Tutorial to generate API key & Secret
Authentication is performed using an API key and secret pair generated from the RelayX Developer Console.
The SDK does not store your credentials on RelayX servers. Authentication is stateless and verified on each connection attempt.
Important behavior to understand:
- Both API key and secret are required.
- Lost API keys & secrets cannot be recovered and must be regenerated.
- There are permissions associated with each key that allow clients to publish, subscribe and read / write from the KV Store. More information here
Authentication happens as part of establishing a connection to the Relay Network.
Connection Lifecycle
When a RelayX client is created, it is ready to connect but may not yet be connected.
Once a connection attempt is made:
- The SDK opens a connection to the Relay Network.
- Authentication is validated using the provided key and secret.
- On success, the client enters the CONNECTED state.
If the network connection drops:
- The SDK automatically enters a reconnecting state.
- Reconnection attempts continue for up to 2 minutes.
- If reconnection succeeds, the client resumes normal operation.
- If reconnection fails, the client disconnects permanently.
API Usage
Initializing the Client
The Swift SDK requires both an API key and secret.
It is strongly recommended to load credentials from environment variables or secure storage.
Client Initialization
import Realtime
do {
// Initialize the Realtime client with credentials
let realtime = try Realtime(apiKey: "<API KEY>", secret: "<SECRET KEY>")
// Prepare the client with configuration options
try realtime.prepare(staging: false, opts: ["debug": false])
// Connect to the Relay Network
try await realtime.connect()
// Client is now ready to publish and subscribe
} catch {
print("Error: \(error)")
}
Important Notes:
- The
Realtimeinitializer can throwRelayError.invalidCredentialsif credentials are invalid. - The
prepare()method configures the client with staging mode and debug options. - The
connect()method is async and must be called withawaitfrom an async context. - Client initialization validates credentials but does not establish a connection.
- The actual connection is established when
connect()is called.
Connection Listeners
The Client SDK provides a set of connection event listeners that allow your application to respond dynamically to changes in connection state. Leveraging these listeners helps you build a more resilient and user-friendly experience by reacting promptly to connectivity events.
Note that on() is an async function and must be called with await from an async context. You'll need to create a class that conforms to the MessageListener protocol to handle events.
-
CONNECTED Fired when the client successfully establishes a connection to the server. Use this event to trigger any initialization logic, enable UI elements, or start sending messages.
class ConnectedListener: MessageListener {
func onMessage(_ message: Any) {
print("Connected to Relay!")
}
}
let connectedListener = ConnectedListener()
try await realtime.on(topic: SystemEvent.connected.rawValue, listener: connectedListener) -
RECONNECT This event covers the lifecycle of reconnection attempts and has multiple states:
- RECONNECTING: Fired when the client begins attempting to restore a lost connection. You can use this to inform users that the app is trying to reconnect.
- RECONNECTED: Fired immediately after the client successfully re-establishes the connection. At this point, queued messages are sent, and subscriptions resume normally.
- RECONN_FAILED: Fired when the client fails to reconnect within the allowed 2-minute window. This signals a permanent disconnection until manual intervention or a new connection attempt.
class ReconnectListener: MessageListener {
func onMessage(_ message: Any) {
if let state = message as? String {
switch state {
case SystemEvent.reconnecting.rawValue:
print("Looks like we're not connected, attempting to reconnect to Relay...")
case SystemEvent.reconnected.rawValue:
print("Reconnected to Relay! :D")
case SystemEvent.reconn_failed.rawValue:
print("Failed to reconnect to Relay :(")
default:
break
}
}
}
}
let reconnectListener = ReconnectListener()
try await realtime.on(topic: SystemEvent.reconnect.rawValue, listener: reconnectListener) -
DISCONNECTED Emitted when the connection is closed intentionally (e.g., client calls
close()) or after failed reconnection attempts. This event can be used to disable message sending, alert users, or trigger cleanup routines.class DisconnectedListener: MessageListener {
func onMessage(_ message: Any) {
print("Connection to Relay closed")
}
}
let disconnectedListener = DisconnectedListener()
try await realtime.on(topic: SystemEvent.disconnected.rawValue, listener: disconnectedListener)
By hooking into these listeners, you gain full visibility and control over the connection lifecycle, enabling responsive UI updates and robust handling of network conditions.
Closing a Connection
The code snippet demonstrates how to close the connection between the client & the Relay Network. Note that close() is an async function and must be called with await from an async context.
try await realtime.close()
Code Example
import Realtime
// Define listener classes
class ConnectedListener: MessageListener {
func onMessage(_ message: Any) {
print("Connected to Relay!")
}
}
class ReconnectListener: MessageListener {
func onMessage(_ message: Any) {
if let state = message as? String {
switch state {
case SystemEvent.reconnecting.rawValue:
print("Looks like we're not connected, attempting to reconnect to Relay...")
case SystemEvent.reconnected.rawValue:
print("Reconnected to Relay! :D")
case SystemEvent.reconn_failed.rawValue:
print("Failed to reconnect to Relay :(")
default:
break
}
}
}
}
class DisconnectedListener: MessageListener {
func onMessage(_ message: Any) {
print("Connection to Relay closed")
}
}
// Main async function
Task {
do {
// Initialize the Realtime client
let realtime = try Realtime(apiKey: "<API KEY>", secret: "<SECRET KEY>")
// Prepare with configuration
try realtime.prepare(staging: false, opts: ["debug": true])
// Set up connection listeners
let connectedListener = ConnectedListener()
try await realtime.on(topic: SystemEvent.connected.rawValue, listener: connectedListener)
let reconnectListener = ReconnectListener()
try await realtime.on(topic: SystemEvent.reconnect.rawValue, listener: reconnectListener)
let disconnectedListener = DisconnectedListener()
try await realtime.on(topic: SystemEvent.disconnected.rawValue, listener: disconnectedListener)
// Connect to Relay
try await realtime.connect()
// Your application logic here
} catch {
print("Error: \(error)")
}
}
This example shows explicit connection handling and how to observe connection state changes.
Failure & Edge Cases
Network Failures
If the network becomes unavailable:
- The SDK automatically attempts to reconnect.
- Reconnection retries continue for up to 2 minutes.
- Messages published during this period are queued locally.
- Queued messages are sent automatically once reconnection succeeds.
If reconnection fails:
- The connection is closed.
- Any queued but unsent messages are discarded.
Subscriber Message Recovery
For subscribers:
- Messages missed during a temporary disconnection are delivered after reconnection.
- Subscriptions are restored automatically.
- Message ordering per topic is preserved.
No manual resubscription is required.
Process Crashes
If the iOS app crashes or exits:
- The connection is closed immediately.
- Queued messages are lost.
- In-flight operations may not complete.
After restart, a new client must be created and connected again.
Common Mistakes
Publishing Without Monitoring Connection State
Publishing while disconnected queues messages temporarily.
If the connection ultimately fails, queued messages are dropped.
Do not assume messages are persisted indefinitely.
Ignoring Connection Events
Connection events provide critical signals for application behavior.
Ignoring them makes debugging network and auth issues significantly harder.
Notes & Limitations
- Secrets cannot be recovered after creation. Store them safely.
- Reconnection behavior is fixed and not configurable.
- Message queuing during reconnect is best-effort and bounded by the reconnect window.
Join our Discord server, post your concern & someone from our team will help you out ✌️