Skip to main content

iOS SDK (Swift)

TgoRTC iOS (Swift) SDK for real-time audio/video communication based on LiveKit.

Installation

Swift Package Manager

  1. In Xcode, select File → Add Packages...
  2. Enter the repository URL:
https://github.com/TgoRTC/TgoRTCiOS
  1. Select Branch: main or a specific version
  2. Add the TgoRTCSDK product

Dependencies

The SDK automatically includes the LiveKit dependency.

Quick Start

1. Initialize SDK

import TgoRTCSDK

let options = Options()
options.isDebug = true
options.mirror = false

TgoRTC.shared.configure(options: options)

2. Create Room Info and Join

import TgoRTCSDK

let roomInfo = RoomInfo(
roomName: "room-name",
token: "your-token",
url: "wss://your-server",
maxParticipants: 4,
rtcType: .video,
isP2P: true,
uidList: ["local-user-id", "remote-user-id"],
timeout: 30,
creatorUid: "creator-user-id",
loginUID: "local-user-id"
)

TgoRTC.shared.roomManager.join(
roomInfo: roomInfo,
micEnabled: true,
cameraEnabled: true,
screenShareEnabled: false
)

Note: The current Swift SDK join(...) method is non-blocking. It returns immediately and connection changes should be observed through addConnectionStatusListener(...).

3. Listen to Connection Status

let connectionToken = TgoRTC.shared.roomManager.addConnectionStatusListener { roomName, status in
print("Room \(roomName) status changed: \(status)")

switch status {
case .connecting:
print("Connecting...")
case .connected:
print("Connected to room")
case .disconnected:
print("Disconnected from room")
}
}

connectionToken.cancel()

Note: The current iOS SDK only exposes connecting / connected / disconnected.

4. Get Participants

let local = TgoRTC.shared.participantManager.getLocalParticipant()
let remotes = TgoRTC.shared.participantManager.getRemoteParticipants()
let all = TgoRTC.shared.participantManager.getAllParticipants()

let newParticipantToken = TgoRTC.shared.participantManager.addNewParticipantListener { participant in
print("New participant joined: \(participant.uid)")
}

5. Media Control and Events

TgoParticipant uses Combine @Published state and PassthroughSubject event streams.

import Combine
import TgoRTCSDK

var cancellables = Set<AnyCancellable>()

let participant = TgoRTC.shared.participantManager.getLocalParticipant()

Task {
await participant?.setMicrophoneEnabled(true)
await participant?.setCameraEnabled(true)
await participant?.setScreenShareEnabled(true)
await participant?.setSpeakerphoneOn(true)
}

participant?.switchCamera()

participant?.$isMicrophoneOn
.sink { isOn in
print("Microphone: \(isOn)")
}
.store(in: &cancellables)

participant?.$isCameraOn
.sink { isOn in
print("Camera: \(isOn)")
}
.store(in: &cancellables)

participant?.$isSpeaking
.sink { speaking in
print("Speaking: \(speaking)")
}
.store(in: &cancellables)

participant?.$audioLevel
.sink { level in
print("Audio level: \(level)")
}
.store(in: &cancellables)

participant?.$connectionQuality
.sink { quality in
print("Connection quality: \(quality)")
}
.store(in: &cancellables)

participant?.$cameraPosition
.sink { position in
print("Camera position: \(position)")
}
.store(in: &cancellables)

participant?.$videoInfo
.sink { info in
print("Video info: \(info.resolutionString), \(info.bitrateString)")
}
.store(in: &cancellables)

participant?.onJoined
.sink {
print("Participant joined")
}
.store(in: &cancellables)

participant?.onLeave
.sink { reason in
print("Participant left, reason: \(reason)")
}
.store(in: &cancellables)

participant?.onTrackPublished
.sink {
print("Track published")
}
.store(in: &cancellables)

participant?.onTrackUnpublished
.sink {
print("Track unpublished")
}
.store(in: &cancellables)

6. Render Video Track

The SDK provides TgoTrackRenderer (a SwiftUI view) for displaying video.

import SwiftUI
import TgoRTCSDK

struct VideoView: View {
@ObservedObject var participant: TgoParticipant

var body: some View {
TgoTrackRenderer(
participant: participant,
source: .camera,
fit: .fill
)
.frame(width: 200, height: 300)
}
}

7. Audio Management

Task {
await TgoRTC.shared.audioManager.toggleSpeakerphone()
await TgoRTC.shared.audioManager.setSpeakerphoneOn(true)
}

Task {
let devices = await TgoRTC.shared.audioManager.getAudioOutputDevices()
print(devices.map { "\($0.name)-\($0.type.rawValue)" })

if let speaker = devices.first(where: { $0.type == .speaker }) {
let success = await TgoRTC.shared.audioManager.selectAudioOutputDevice(speaker)
print("Switch result: \(success)")
}
}

let deviceToken = TgoRTC.shared.audioManager.addDeviceChangeListener { devices in
print("Audio devices changed: \(devices)")
}

8. Leave Room

Task {
await TgoRTC.shared.roomManager.leaveRoom()
}

API Reference

TgoRTC

PropertyTypeDescription
sharedTgoRTCSingleton instance
optionsOptions?SDK configuration
roomManagerRoomManagerRoom management
participantManagerParticipantManagerParticipant management
audioManagerAudioManagerAudio management

RoomManager

MethodDescription
join(roomInfo, micEnabled, cameraEnabled, screenShareEnabled)Join a room
leaveRoom()Leave current room
addConnectionStatusListener(listener)Add connection status listener
addVideoInfoListener(listener)Add local video info listener
restartTimeoutCheckerIfNeeded()Restart the timeout checker if needed
PropertyTypeDescription
currentRoomInfoRoomInfo?Current room info
roomRoom?LiveKit room instance

ParticipantManager

MethodDescription
getLocalParticipant()Get local participant
getRemoteParticipants()Get remote participants
getAllParticipants()Get all participants
initializePendingParticipants()Initialize pending participants
syncExistingRemoteParticipants()Sync existing remote participants
inviteParticipant(roomName, uids)Invite participants
removePendingParticipants(roomName, uids)Remove pending participants
setParticipantJoin(participant)Mark a participant as joined
setParticipantLeave(participant)Mark a participant as left
addNewParticipantListener(listener)Listen for new participants
clear()Clear participant state

TgoParticipant

Properties

PropertyTypeDescription
uidStringParticipant ID
createdAtDateCreation time
isLocalBoolWhether this is the local participant
isDisposedBoolWhether this participant wrapper is disposed

@Published Properties

PropertyTypeDescription
isMicrophoneOnBoolWhether the microphone is on
isCameraOnBoolWhether the camera is on
isSpeakingBoolWhether the participant is speaking
audioLevelFloatAudio level
connectionQualityTgoConnectionQualityConnection quality
cameraPositionTgoCameraPositionCamera position
isJoinedBoolWhether the participant has joined
videoInfoVideoInfoCurrent video info

Events

PropertyTypeDescription
onJoinedPassthroughSubject<Void, Never>Joined event
onLeavePassthroughSubject<TgoLeaveReason, Never>Leave event
onTrackPublishedPassthroughSubject<Void, Never>Track published event
onTrackUnpublishedPassthroughSubject<Void, Never>Track unpublished event

Methods

MethodDescription
getVideoTrack(source)Get video track for a specific source
setLocalParticipant(participant)Bind local participant
setRemoteParticipant(participant)Bind remote participant
notifyLeave(reason)Notify leave
setSpeakerphoneOn(enabled)Set speakerphone
setMicrophoneEnabled(enabled)Enable/disable microphone
setCameraEnabled(enabled)Enable/disable camera
setScreenShareEnabled(enabled)Enable/disable screen share
switchCamera()Switch front/back camera
dispose()Release resources

AudioManager

Method / PropertyDescription
isSpeakerOnWhether speakerphone is currently on
currentOutputDeviceCurrently selected output device
addDeviceChangeListener(listener)Listen for device changes
setSpeakerphoneOn(on, forceSpeakerOutput)Set speakerphone
toggleSpeakerphone()Toggle speakerphone
getAudioInputDevices()Get audio input devices
getAudioOutputDevices()Get audio output devices
selectAudioOutputDevice(device)Select an output device
dispose()Clean up resources

TgoTrackRenderer

TgoTrackRenderer(
participant: participant,
source: .camera,
fit: .fill
)

Enums

ConnectStatus

public enum ConnectStatus {
case connecting
case connected
case disconnected
}

RTCType

public enum RTCType {
case audio
case video
}

TgoCameraPosition

public enum TgoCameraPosition {
case front
case back
}

TgoConnectionQuality

public enum TgoConnectionQuality {
case unknown
case excellent
case good
case poor
case lost
}

TgoLeaveReason

public enum TgoLeaveReason {
case normal
case timeout
}

RoomInfo

public final class RoomInfo {
var roomName: String
var token: String
var url: String
var maxParticipants: Int
var rtcType: RTCType
var isP2P: Bool
var uidList: [String]
var timeout: Int
var creatorUid: String
var loginUID: String

func isCreator() -> Bool
func getP2PToUID() -> String
}

VideoInfo

public struct VideoInfo {
let width: Int
let height: Int
let bitrate: Int
let frameRate: Double
let layerId: String?
let qualityLimitationReason: String?

var isValid: Bool
var resolutionString: String
var bitrateString: String
}

Platform Configuration

Permissions

Add the following descriptions to your Info.plist:

<key>NSCameraUsageDescription</key>
<string>Need camera access for video calls</string>
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for audio calls</string>

To support background audio calls, enable Background Modes in Signing & Capabilities and check Audio, AirPlay, and Picture in Picture.