Preview. The Swift Package source is in private development — passes 21/21 unit tests — and a public Swift Package + CocoaPods release is on the v0.3 roadmap. The API surface below is stable; only the distribution channel is in flight. Need source access during the preview window? Email [email protected].
The iOS SDK is a Swift Package mirroring the @levelchat/web concepts — LevelChat, Room, tracks, events — adapted to Swift Concurrency (async/await, actor, AsyncStream). Naming follows Swift idiom: events are a RoomEvent enum iterated with for await, not Web's kebab-case event strings. See the SDK parity matrix for the full per-platform naming table.
MIT-licensed. Real WebRTC via stasel/WebRTC.
Install — preview
The public install commands below are the planned shapes for v0.3; they will resolve once the Swift Package Index publish lands.
// dependencies: [
// .package(url: "https://swift.levelchat.io/level-vchat.git", from: "0.3.0"),
// ]
// TODO(v0.3): the canonical SPM URL above is the planned mirror; the
// public Git endpoint is set up alongside the v0.3 release.# pod 'LevelChat', '~> 0.3'During the preview window the package is distributed under NDA — email
[email protected] with your use case and we'll send back
the private mirror credentials + a Package.swift snippet you can drop into your project.
Permissions
Add to Info.plist:
<key>NSCameraUsageDescription</key>
<string>Used for video calls</string>
<key>NSMicrophoneUsageDescription</key>
<string>Used for voice + video calls</string>For screen sharing, also add a Broadcast Upload Extension target to your app. The SDK's ScreenShare module drives RPSystemBroadcastPickerView for the picker UI and bridges the extension's frames into room.publishScreen().
The end-to-end path
Every LevelChat SDK follows the same five steps: install → token → join → publish → handle events → leave. Here it is for Swift.
1. Mint a token
In production the token comes from your backend. Never ship an apiKey in the binary — your
auth flow mints short-lived JWTs scoped to a single (room, user, role). The issueToken
helper exists only for getting-started tutorials. See the Quickstart for the
POST /v1/auth/tokens/room request shape.
let token = try await fetchTokenFromYourBackend()2. Construct the client + join
LevelChat is an actor, so construction and joins are awaited. joinRoom returns the
Room actor directly; joinLive wraps it in a LiveStream carrying the role hint:
import LevelChat
let client = try await LevelChat(config: LevelChatConfig(
region: "eu-fsn",
logLevel: .info
))
// Either join a room directly…
let room = try await client.joinRoom(JoinRoomOptions(token: token))
// …or join as a typed broadcaster / viewer:
let live = try await client.joinLive(
JoinLiveOptions(token: token, role: .broadcaster)
)
let room = live.roomJoinRoomOptions mirrors the web SDK: token (required), device, preferredCodec,
simulcast (default true), svc, e2eeKey, signalingUrl.
3. Handle events
room.events is an AsyncStream<RoomEvent> — Swift's idiomatic typed event bus. Iterate it
with for await from any task; the RoomEvent enum carries the same names as the web SDK:
Task {
for await event in await room.events {
switch event {
case .participantJoined(let p):
print("joined: \(p.identity)")
case .participantLeft(let p, _):
print("left: \(p.identity)")
case .trackSubscribed(let track):
// A remote track is ready — bind track.nativeTrackId to a view.
attachRenderer(track)
case .trackUnsubscribed(let track):
detachRenderer(track)
case .connectionState(let state):
// state ∈ .connecting | .connected | .reconnecting | .disconnected | .failed
updateConnectionBanner(state)
case .connectionQuality(let participant, let score):
// score.label ∈ excellent | good | fair | poor | disconnected
updateNetworkIndicator(participant, score)
case .chatMessage(let m):
appendChatMessage(m)
default:
break
}
}
}4. Publish camera + mic
Both methods are async and return a TrackView whose nativeTrackId you bind to your
renderer for the local preview:
let camera = try await room.publishCamera(PublishCameraOptions(resolution: "720p"))
let mic = try await room.publishMic()PublishCameraOptions / PublishMicOptions mirror the web SDK — deviceId for device
selection, encodings for simulcast layers, echoCancellation / noiseSuppression /
autoGainControl on the mic.
5. Chat + recording
In-call chat rides the room's data channel — sendChat to send, .chatMessage events to
receive (wired in step 3):
try await room.sendChat(text: "hello room")Recording is host-only and server-side. Start it with room.startRecording(); it returns a
RecordingView whose id you can surface in your UI:
let recording = try await room.startRecording(
StartRecordingOptions(layout: .speaker)
)
// …
_ = try await room.stopRecording()Recording is gated by the
recordcapability on the token — a UI-only "host" flag is not enough. On Web the canonical method isroom.record({ compose }); on iOS thestartRecordingname +layoutoption is current. See the Recordings guide.
6. Leave
leave() tears down the peer connection and signaling, and finishes the event stream:
await room.leave()UI Kit
The package ships a second product, LevelChatUI, with SwiftUI views matching the React component kit's component set. It has no WebRTC dependency — safe to import from marketing apps and previews.
import LevelChatUI
VideoTileView(
name: "Mert",
quality: .good,
micOn: true,
isSpeaking: speaker == myId
)
.frame(width: 240, height: 180)LevelChatUI ships VideoTileView, ControlBarView, PreJoinView, SpeakerStageView, ChatView, PollPanelView, QAPanelView, and WhiteboardView — every component the React web kit exports has a SwiftUI counterpart with the same name and brand tokens.
Public API
actor LevelChat— the entrypoint; construct withtry await LevelChat(config:).joinRoom/joinLive/issueToken(dev only).actor Room—join/leave/publishCamera/publishMic/publishScreen/stopPublishing/subscribe/unsubscribe/sendChat/startRecording/stopRecording.room.events: AsyncStream<RoomEvent>— typed event surface (participantJoined,trackSubscribed,connectionState,connectionQuality,chatMessage, …); iterate it withfor await.
Modules: Errors, Logger, Events, Constants, Region, Quality, Encryption, Devices, Signaling, Peer, Track, Publication, Subscription, Participant, Room, AudioSession, Lifecycle, ScreenShare.
Audio session
Auto-configured for voice calls — AVAudioSession.playAndRecord + .voiceChat + .allowBluetooth + .defaultToSpeaker. Override via AudioSession.applyMode(...) if your app needs a different routing mode.
Lifecycle
Built-in UIApplication/NSApplication background-foreground observer via the
LifecycleObserver actor — the Room pauses local video when the app is
backgrounded. Call observer.start() / observer.stop() if you need to drive
it yourself.
License
The SDK itself is MIT-licensed — embed in any app, commercial or personal, no royalties. The LevelChat server (signaling, SFU, recording) is BUSL-licensed and requires a paid commercial license for production use beyond the trial threshold. See Licensing.