Documentation
Sign inGet started

Swift SDK

BirdRealtime is the official Swift client for Bird Realtime: it holds a WebSocket to the Bird edge, subscribes to channels, and delivers events as they are published. It is hand-written rather than generated, because Realtime is a WebSocket data plane rather than a REST surface, and it speaks the same wire dialect as the browser client.
It has no dependencies (URLSessionWebSocketTask for transport, JSONSerialization for frames) and runs on iOS 15+, macOS 12+, tvOS 15+, watchOS 8+, and Linux.
This client is the receiving side. Publishing events, authorizing channels, and terminating connections are server-side operations on the Bird API; see publishing events.

Install

In Xcode, use File › Add Package Dependencies with the repository URL. In a Package.swift:
Codevoorbeeld
dependencies: [
    .package(url: "https://github.com/messagebird/bird-sdk-swift.git", from: "0.1.0")
]
Then add the product to the target that uses it:
Codevoorbeeld
.target(
    name: "YourApp",
    dependencies: [.product(name: "BirdRealtime", package: "bird-sdk-swift")]
)

Construct a client

Codevoorbeeld
import BirdRealtime

let bird = BirdRealtime(options: .init(
    appKey: "your-app-key",
    region: "us1"
))
Only appKey is required, plus either region (us1 or eu1, which resolves the edge host) or an explicit wsHost. The app key is public and ships in client code: it identifies the app, it does not authorize anything. Handlers run on the main queue unless you pass a deliveryQueue.

Subscribe to a channel

Codevoorbeeld
let orders = bird.subscribe("orders")
orders.bind("order-updated") { data in
    print("order changed:", data ?? "")
}
A channel whose name has no private- or presence- prefix is public: any client with the app key can subscribe, so treat its contents as readable by anyone who can open your app.

Private and presence channels

Private and presence channels are authorized by your backend, which holds the app secret. Point the client at your endpoint: it POSTs {"connection_id", "channel_name"} and expects {"auth", "member_data"?} back.
Codevoorbeeld
let bird = BirdRealtime(options: .init(
    appKey: "your-app-key",
    region: "us1",
    authEndpoint: URL(string: "https://your-backend.example.com/bird/auth")!,
    authHeaders: ["authorization": "Bearer <session token>"]
))

guard let room = bird.subscribe("presence-room-42") as? PresenceChannel else { return }
room.bind(BirdProtocol.Event.subscriptionSucceeded) { _ in
    print("me:", room.myId ?? "?", "members:", Array(room.members.keys))
}
room.bind(BirdProtocol.Event.memberAdded) { member in
    print("joined:", member ?? "")
}
Pass an authorizer closure instead to sign through your own networking stack.
A rejection from the server (a bad signature, an app over capacity) arrives on the connection rather than the channel, because the wire carries no channel attribution. Observe those with onError. A failure inside your authorizer does emit bird:subscription_error on the channel.

Sign in a member

signin() tells the edge which member this connection belongs to. That identity is what lets the API address a member or terminate their connections, and it is what satisfies an app configured to require authorized connections.
Codevoorbeeld
let bird = BirdRealtime(options: .init(
    appKey: "your-app-key",
    region: "us1",
    memberAuthEndpoint: URL(string: "https://your-backend.example.com/bird/auth/member")!
))

let me = try await bird.signin()
print("signed in as", me.memberId)

bird.member.bind("order.shipped") { data in
    print("addressed to me:", data ?? "")
}
Call signin() once. The identity lives on the connection, so the client signs in again automatically after a reconnect; a failed re-signin has no call to throw from and surfaces on onSigninError instead, which keeps a failing member endpoint from disturbing channel subscriptions.

Connection lifecycle

Codevoorbeeld
bird.onConnectionStateChange { previous, current in
    print("connection: \(previous)\(current)")
}
bird.onError { error in
    if error.code == 4009 { print("session ended elsewhere") }
}
Reconnection is automatic, with full-jitter exponential backoff from 1s to a 30s cap. Close codes in 4000–4099 are refusals and terminal: the client stops and reports the code, because a refusal is a decision rather than a transient failure. Codes in 4200–4299 reconnect immediately; anything else backs off. Channels re-subscribe with fresh authorization on every reconnect. See connection lifecycle.
Transport is TLS for every non-loopback host. allowInsecure permits ws:// only for localhost, 127.0.0.1 and [::1], so a config copied from a development target cannot silently downgrade production traffic.

Client events

With the app's client-events setting enabled, a client subscribed to a private or presence channel can publish to its peers directly:
Codevoorbeeld
try room.trigger("client-typing", data: ["on": true])

Source

github.com/messagebird/bird-sdk-swift, MIT licensed. Report issues there.