> ## Documentation Index
> Fetch the complete documentation index at: https://red5.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Red5 iOS SDK — Native WebRTC Streaming for iOS

> Add native WebRTC streaming to iOS with Red5WebRTCKit. Covers installation, license setup, publish and subscribe flows, video settings, and camera controls.

`Red5WebRTCKit` is the native iOS SDK for publishing and subscribing to live streams over WebRTC. It handles ICE negotiation, codec selection, camera and microphone capture, and license validation so you can focus on building your app. The SDK works with both Red5 Cloud (Stream Manager) deployments and standalone Red5 Pro servers, and is compatible with SwiftUI and UIKit.

## Requirements

* iOS 13.0 or later
* Xcode 14.0 or later
* Swift 5.5 or later
* A valid Red5 Pro SDK license key

## Installation

Add the SDK to your Xcode project using Swift Package Manager.

**Package URL:** `https://github.com/red5pro/red5pro-ios-sdk`

Or declare it in your `Package.swift`:

```swift theme={null}
dependencies: [
    .package(url: "https://github.com/red5pro/red5pro-ios-sdk", from: "latest")
]
```

## Permissions

Add the following keys to your `Info.plist` before publishing. Camera permission is not required for audio-only sessions.

```xml theme={null}
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to stream video</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access to stream audio</string>
```

## License key setup

Pass your license key to the builder with `.setLicenseKey()`. The SDK validates the key asynchronously when the client is built, and fires `onLicenseValidated` on your delegate before any streaming can begin.

```swift theme={null}
let client = Red5WebrtcClientBuilder()
    .setLicenseKey("XXXX-XXXX-XXXX-XXXX")
    // ... other configuration
    .build()
```

<Note>
  Your license key is available on the **Dev Resources** page in the [Red5 Cloud dashboard](https://cloud.red5.net).
</Note>

## Minimal publish

Publishing requires three steps after the license validates: start a camera preview, then call `publish()`.

<Steps>
  <Step title="Build the client">
    Configure the client with your server details and media settings. Use `setStreamManagerHost()` for Red5 Cloud or `setServerIp()` for a standalone server.

    ```swift theme={null}
    import WebRTC
    import Red5WebRTCKit

    var client: Red5WebrtcClient?
    let renderer = RTCMTLVideoView()

    client = Red5WebrtcClientBuilder()
        .setStreamManagerHost("userid-xxx.cloud.red5.net") // Red5 Cloud
        // .setServerIp("192.168.1.100")                  // Standalone
        .setPort(443)
        .setAppName("live")
        .setStreamName("myStream")
        .setLicenseKey("XXXX-XXXX-XXXX-XXXX")
        .setVideoEnabled(true)
        .setAudioEnabled(true)
        .setEventListener(self)
        .build()

    client?.setVideoRenderer(renderer)
    ```
  </Step>

  <Step title="Wait for license validation">
    The SDK fires `onLicenseValidated` before any other callback. Start the camera preview only after validation succeeds.

    ```swift theme={null}
    extension YourClass: Red5ProWebrtcEventDelegate {
        func onLicenseValidated(validated: Bool, message: String) {
            if validated {
                client?.startPreview()
            } else {
                print("License invalid: \(message)")
            }
        }
    }
    ```
  </Step>

  <Step title="Publish when preview is ready">
    Enable your publish button in `onPreviewStarted`, then call `publish()` when the user taps it.

    ```swift theme={null}
    func onPreviewStarted() {
        // Unlock your UI
        isReady = true
    }

    // Called from a button tap:
    client?.publish()
    ```
  </Step>

  <Step title="Stop the session">
    ```swift theme={null}
    client?.stopPublish()
    client?.stopPreview()
    ```
  </Step>
</Steps>

**Publish lifecycle:**

```
Red5WebrtcClientBuilder.build()
        ↓
onLicenseValidated(validated: true)
        ↓
client.startPreview()
        ↓
onPreviewStarted()          ← enable Publish button here
        ↓
client.publish()
        ↓
onPublishStarted()          ← stream is live
```

## Minimal subscribe

Subscribing does not require a preview step. Build the client, attach a renderer for the incoming video, and call `subscribe()`.

```swift theme={null}
client = Red5WebrtcClientBuilder()
    .setStreamManagerHost("userid-xxx.cloud.red5.net")
    .setPort(443)
    .setAppName("live")
    .setStreamName("myStream")
    .setLicenseKey("XXXX-XXXX-XXXX-XXXX")
    .setEventListener(self)
    .build()

client?.setVideoRenderer(renderer)
client?.subscribe()
```

Show the video renderer once the stream arrives:

```swift theme={null}
func onSubscribeStarted() {
    hasVideo = true  // show RTCMTLVideoView in your UI
}
```

## Audio-only streaming

Set `.setVideoEnabled(false)` to publish or subscribe without video. No camera permission is required, and no `RTCMTLVideoView` renderer is needed.

```swift theme={null}
client = Red5WebrtcClientBuilder()
    .setServerIp(Config.serverIP)
    .setPort(Config.port)
    .setAppName(Config.appName)
    .setStreamName(Config.streamName)
    .setLicenseKey(Config.licenseKey)
    .setVideoEnabled(false)  // disable video track
    .setAudioEnabled(true)
    .setEventListener(self)
    .build()

// No setVideoRenderer() call needed
```

For audio-only publishing, call `publish()` directly from `onLicenseValidated` — no preview step is needed:

```swift theme={null}
func onLicenseValidated(validated: Bool, message: String) {
    if validated {
        client?.publish()  // no startPreview() needed
    }
}
```

## Custom video settings

Configure resolution, frame rate, and bitrate on the builder before calling `.build()`. These settings cannot be changed after the client is created.

```swift theme={null}
client = Red5WebrtcClientBuilder()
    // ... connection config ...
    .setVideoWidth(1280)    // pixels
    .setVideoHeight(720)    // pixels
    .setVideoFps(30)        // frames per second
    .setVideoBitrate(1500)  // kilobits per second
    .build()
```

**Recommended presets:**

| Quality    | Width | Height | FPS | Bitrate   |
| ---------- | ----- | ------ | --- | --------- |
| 360p       | 640   | 360    | 30  | 400 kbps  |
| 480p       | 854   | 480    | 30  | 750 kbps  |
| 720p       | 1280  | 720    | 30  | 1500 kbps |
| 720p 60fps | 1280  | 720    | 60  | 2500 kbps |
| 1080p      | 1920  | 1080   | 30  | 3000 kbps |

<Warning>
  Bitrate is a target, not a guarantee. WebRTC's congestion control may lower it dynamically if network conditions degrade. To change settings after a session starts, stop and release the client, then rebuild with new parameters.
</Warning>

## Camera controls

Call these methods on the client instance any time after `startPreview()` completes.

**Switch between front and back camera:**

```swift theme={null}
client?.switchCamera()
```

**Mute and unmute video:**

```swift theme={null}
client?.setVideoEnabled(false) // viewer sees a black/frozen frame
client?.setVideoEnabled(true)  // live camera resumes
```

**Mute and unmute audio:**

```swift theme={null}
client?.setAudioEnabled(false) // viewer hears silence
client?.setAudioEnabled(true)  // microphone resumes
```

**SwiftUI pattern:**

```swift theme={null}
@State private var videoMuted = false
@State private var audioMuted = false

Button("Mute Video") {
    videoMuted.toggle()
    client?.setVideoEnabled(!videoMuted)
}

Button("Flip Camera") {
    client?.switchCamera()
}
```

## Event reference

Implement `Red5ProWebrtcEventDelegate` in your class to receive SDK lifecycle events. All methods have default no-op implementations, so you only need to override the callbacks you care about.

<AccordionGroup>
  <Accordion title="Publish events">
    | Callback                         | Description                          |
    | -------------------------------- | ------------------------------------ |
    | `onPublishStarted()`             | Publish session started successfully |
    | `onPublishStopped()`             | Publish session ended                |
    | `onPublishFailed(error: String)` | Publish session failed               |
  </Accordion>

  <Accordion title="Subscribe events">
    | Callback                           | Description                            |
    | ---------------------------------- | -------------------------------------- |
    | `onSubscribeStarted()`             | Subscribe session started successfully |
    | `onSubscribeStopped()`             | Subscribe session ended                |
    | `onSubscribeFailed(error: String)` | Subscribe session failed               |
  </Accordion>

  <Accordion title="Connection and lifecycle events">
    | Callback                                                 | Description                                  |
    | -------------------------------------------------------- | -------------------------------------------- |
    | `onPreviewStarted()`                                     | Local camera preview is running              |
    | `onPreviewStopped()`                                     | Local camera preview stopped                 |
    | `onLicenseValidated(validated: Bool, message: String)`   | License check completed — always fires first |
    | `onIceConnectionStateChanged(state: IceConnectionState)` | ICE connection state changed                 |
    | `onConnectionStateChanged(state: PeerConnectionState)`   | Peer connection state changed                |
    | `onError(error: String)`                                 | An unrecoverable error occurred              |
  </Accordion>

  <Accordion title="Chat events">
    | Callback                                                  | Description                                |
    | --------------------------------------------------------- | ------------------------------------------ |
    | `onChatConnected()`                                       | Connected to the PubNub chat service       |
    | `onChatDisconnected()`                                    | Disconnected from the PubNub chat service  |
    | `onChatMessageReceived(channel: String, message: Any)`    | Message received on the subscribed channel |
    | `onChatSendSuccess(channel: String, timetoken: NSNumber)` | Message sent successfully                  |
    | `onChatSendError(channel: String, errorMessage: String)`  | Message failed to send                     |
  </Accordion>
</AccordionGroup>

## Configuration reference

Key `Red5WebrtcClientBuilder` methods:

| Method                                 | Default  | Description                        |
| -------------------------------------- | -------- | ---------------------------------- |
| `setStreamManagerHost(_ host: String)` | —        | Stream Manager host for Red5 Cloud |
| `setServerIp(_ ip: String)`            | —        | Standalone server IP address       |
| `setPort(_ port: Int)`                 | `443`    | Server port                        |
| `setAppName(_ name: String)`           | `"live"` | Application scope name             |
| `setStreamName(_ name: String)`        | —        | Stream name                        |
| `setLicenseKey(_ key: String)`         | —        | Red5 Pro license key               |
| `setVideoEnabled(_ enabled: Bool)`     | `true`   | Enable or disable video            |
| `setAudioEnabled(_ enabled: Bool)`     | `true`   | Enable or disable audio            |
| `setVideoWidth(_ width: Int)`          | `640`    | Capture width in pixels            |
| `setVideoHeight(_ height: Int)`        | `480`    | Capture height in pixels           |
| `setVideoFps(_ fps: Int)`              | `30`     | Frame rate                         |
| `setVideoBitrate(_ bitrate: Int)`      | `500`    | Bitrate in kbps                    |
| `setEventListener(_ delegate)`         | —        | SDK event delegate                 |

## Example apps

Each example is a self-contained Xcode project. Copy `Config.swift` and the example file into a fresh project, fill in your server details, and run.

* **01-MinimalPublish** — fewest lines needed to go live
* **02-MinimalSubscribe** — fewest lines needed to receive a stream
* **03-AudioOnly** — publish or subscribe with video disabled
* **04-CustomVideoSettings** — configure resolution, frame rate, and bitrate
* **05-CameraControls** — flip camera, mute/unmute video and audio while live

[Browse all examples on GitHub](https://github.com/red5pro/red5pro-ios-sdk-examples)
