Skip to main content
TrackPublication represents a track that has been published to a room. It contains metadata about the track and provides access to the underlying media track.

Overview

There are two types of track publications:
  • LocalTrackPublication - for tracks published by the local participant
  • RemoteTrackPublication - for tracks published by remote participants
Both extend the base TrackPublication class and are accessed through participant objects.

Properties

sid
Track.Sid
Server-assigned unique identifier for the track
kind
Track.Kind
Type of track (.audio or .video)
source
Track.Source
Source of the track (.camera, .microphone, .screenShareVideo, .screenShareAudio, .unknown)
name
String
Name of the track
track
Track?
The underlying media track (available when subscribed)
isMuted
Bool
Whether the track is currently muted
streamState
StreamState
Current streaming state (.active, .paused, .unknown)
dimensions
Dimensions?
Video dimensions (width and height) for video tracks
isSimulcasted
Bool
Whether the track uses simulcast (multiple quality layers)
mimeType
String
MIME type of the track (e.g., “audio/opus”, “video/vp8”)
isSubscribed
Bool
Whether the track is currently subscribed (always true for local tracks)
encryptionType
EncryptionType
Type of encryption applied to the track (.none, .gcm, .custom)

LocalTrackPublication

LocalTrackPublication represents tracks published by the local participant.

Properties

streamState
StreamState
Always returns .active for local tracks

Methods

mute()

Mutes the local track. Throws: Error if muting fails
if let publication = room.localParticipant.getTrackPublication(source: .camera) as? LocalTrackPublication {
    try await publication.mute()
}

unmute()

Unmutes the local track. Throws: Error if unmuting fails
if let publication = room.localParticipant.getTrackPublication(source: .microphone) as? LocalTrackPublication {
    try await publication.unmute()
}

Usage Example

// Publish a camera track
let videoTrack = LocalVideoTrack.createCameraTrack()
let publication = try await room.localParticipant.publish(videoTrack: videoTrack)

print("Published track: \(publication.name)")
print("Track SID: \(publication.sid)")
print("Is muted: \(publication.isMuted)")

// Mute the track later
try await publication.mute()

RemoteTrackPublication

RemoteTrackPublication represents tracks published by remote participants.

Properties

isSubscriptionAllowed
Bool
Whether subscription to this track is permitted by the publisher
isEnabled
Bool
Whether the track is enabled on the server (affects bandwidth)
subscriptionState
SubscriptionState
Current subscription state (.subscribed, .unsubscribed, .notAllowed)

Methods

set(subscribed:)

Subscribe or unsubscribe from a remote track.
subscribed
Bool
required
true to subscribe, false to unsubscribe
Throws: Error if subscription change fails
if let publication = participant.getTrackPublication(source: .camera) as? RemoteTrackPublication {
    try await publication.set(subscribed: true)
}

set(enabled:)

Enable or disable the track on the server to save bandwidth.
enabled
Bool
required
true to enable, false to disable
Throws: Error if operation fails Useful when a participant is off-screen and you want to reduce bandwidth usage.
// Disable video when participant is off-screen
if let videoPublication = participant.getTrackPublication(source: .camera) as? RemoteTrackPublication {
    try await videoPublication.set(enabled: false)
}

set(preferredFPS:)

Set the preferred frame rate for a video track.
preferredFPS
UInt
required
Desired frames per second
Throws: Error if operation fails
if let videoPublication = participant.getTrackPublication(source: .camera) as? RemoteTrackPublication {
    try await videoPublication.set(preferredFPS: 15)
}

set(preferredDimensions:)

Set the preferred video dimensions.
preferredDimensions
Dimensions
required
Desired width and height
Throws: Error if operation fails The server will select the appropriate simulcast layer based on this value.
if let videoPublication = participant.getTrackPublication(source: .camera) as? RemoteTrackPublication {
    let dimensions = Dimensions(width: 640, height: 480)
    try await videoPublication.set(preferredDimensions: dimensions)
}

set(videoQuality:)

Set the maximum video quality for simulcast tracks.
videoQuality
VideoQuality
required
Desired quality (.low, .medium, .high)
Throws: Error if operation fails
if let videoPublication = participant.getTrackPublication(source: .camera) as? RemoteTrackPublication {
    try await videoPublication.set(videoQuality: .high)
}

Usage Example

func room(_ room: Room, participant: RemoteParticipant, didPublishTrack publication: RemoteTrackPublication) {
    print("Track published: \(publication.name)")
    print("Kind: \(publication.kind)")
    print("Source: \(publication.source)")
    print("Dimensions: \(publication.dimensions?.description ?? "none")")
    
    // Subscribe to video tracks
    if publication.kind == .video {
        Task {
            try await publication.set(subscribed: true)
        }
    }
}

func room(_ room: Room, participant: RemoteParticipant, didSubscribeTrack publication: RemoteTrackPublication) {
    if let videoTrack = publication.track as? VideoTrack {
        DispatchQueue.main.async {
            self.videoView.track = videoTrack
        }
    }
}

Adaptive Stream

The SDK supports adaptive streaming, which automatically adjusts video quality based on the view size:
// Enable adaptive streaming in RoomOptions
let roomOptions = RoomOptions(
    adaptiveStream: true
)

let room = Room(roomOptions: roomOptions)
When adaptive streaming is enabled:
  • Video tracks are automatically enabled/disabled based on whether they’re visible
  • Video quality is adjusted based on the view size
  • Bandwidth is optimized automatically

Delegate Events

Track publication events are delivered through RoomDelegate and ParticipantDelegate:
extension MyClass: RoomDelegate {
    func room(_ room: Room, participant: Participant, trackPublication: TrackPublication, didUpdateIsMuted isMuted: Bool) {
        print("Track \(trackPublication.name) muted: \(isMuted)")
    }
    
    func room(_ room: Room, participant: RemoteParticipant, trackPublication: RemoteTrackPublication, didUpdateStreamState streamState: StreamState) {
        print("Stream state: \(streamState)")
    }
    
    func room(_ room: Room, participant: RemoteParticipant, trackPublication: RemoteTrackPublication, didUpdateIsSubscriptionAllowed isAllowed: Bool) {
        print("Subscription allowed: \(isAllowed)")
    }
}

Common Patterns

Finding Specific Tracks

// Find camera track
if let cameraPublication = participant.getTrackPublication(source: .camera) {
    print("Camera track found: \(cameraPublication.name)")
}

// Find all video tracks
for publication in participant.videoTracks {
    print("Video track: \(publication.name), source: \(publication.source)")
}

// Find by track SID
if let publication = participant.trackPublications[trackSid] {
    print("Found publication: \(publication.name)")
}

Managing Bandwidth

// Disable tracks for off-screen participants
func participantWentOffScreen(_ participant: RemoteParticipant) {
    for publication in participant.videoTracks {
        if let remotePublication = publication as? RemoteTrackPublication {
            Task {
                try await remotePublication.set(enabled: false)
            }
        }
    }
}

// Re-enable when participant comes back on screen
func participantCameOnScreen(_ participant: RemoteParticipant) {
    for publication in participant.videoTracks {
        if let remotePublication = publication as? RemoteTrackPublication {
            Task {
                try await remotePublication.set(enabled: true)
            }
        }
    }
}

See Also

Build docs developers (and LLMs) love