The app and its broadcast extension retain their original iOS 14 minimum. APIExample_SwiftUIApp
uses the SwiftUI App lifecycle and WindowGroup; do not add a UIKit app/scene delegate.
UIKit presentation helpers resolve key windows from foreground UIWindowScene instances.
| Case | Path | Key APIs | Description |
|---|---|---|---|
| JoinChannelVideo | Examples/Basic/JoinChannelVideo/ |
joinChannel(), setupLocalVideo(), setupRemoteVideo() |
Basic video call — join channel and render local/remote video |
| JoinChannelVideo(Token) | Examples/Basic/JoinChannelVideo(Token)/ |
joinChannel(byToken:), setupLocalVideo(), setupRemoteVideo() |
Video call with token authentication |
| JoinChannelVideo(Recorder) | Examples/Basic/JoinChannelVideo(Recorder)/ |
createMediaRecorder(), joinChannel(), setupLocalVideo() |
Local and remote stream recording |
| JoinChannelAudio | Examples/Basic/JoinChannelAudio/ |
joinChannel(), setAudioProfile(), setAudioScenario(), adjustRecordingSignalVolume(), enable(inEarMonitoring:) |
Basic audio call with profile, scenario, and volume controls |
| LiveStreaming | Examples/Advanced/LiveStreaming/ |
setClientRole(), setVideoScenario(), preloadChannel(), enableInstantMediaRendering() |
Interactive live streaming with role switching |
| RTMPStream | Examples/Advanced/RTMPStream/ |
startRtmpStreamWithoutTranscoding(), startRtmpStream(withTranscoding:), updateRtmpTranscoding(), stopRtmpStream() |
Push stream to CDN with optional transcoding |
| VideoMetadata | Examples/Advanced/VideoMetadata/ |
setMediaMetadataDataSource(), setMediaMetadataDelegate() |
Send and receive metadata attached to video stream |
| VoiceChanger | Examples/Advanced/VoiceChanger/ |
setVoiceBeautifierPreset(), setAudioEffectPreset(), setVoiceConversionPreset(), setLocalVoiceFormant() |
Voice beautifier, effects, and conversion presets |
| CustomPCMAudioSource | Examples/Advanced/CustomPCMAudioSource/ |
createCustomAudioTrack(), enableCustomAudioLocalPlayback(), pushExternalAudioFrameRawData() |
Push custom PCM audio frames as external audio source |
| CustomAudioRender | Examples/Advanced/CustomAudioRender/ |
enableExternalAudioSink(), pullPlaybackAudioFrameRawData() |
Pull audio frames for custom rendering |
| RawAudioData | Examples/Advanced/RawAudioData/ |
setAudioFrameDelegate(), sendAudioMetadata() |
Capture raw audio PCM data via delegate |
| RawVideoData | Examples/Advanced/RawVideoData/ |
setVideoFrameDelegate() |
Capture raw video frames via delegate |
| PictureInPicture | Examples/Advanced/PictureInPicture/ |
AVPictureInPictureController, joinChannel(), setVideoFrameDelegate() |
Picture-in-Picture using AVKit (iOS 15+) |
| QuickSwitchChannel | Examples/Advanced/QuickSwitchChannel/ |
joinChannel(), leaveChannel() |
Quickly switch between channels as audience |
| JoinMultiChannel | Examples/Advanced/JoinMultiChannel/ |
joinChannelEx(), takeSnapshotEx() |
Join multiple channels simultaneously via ex connection |
| StreamEncryption | Examples/Advanced/StreamEncryption/ |
enableEncryption() |
Built-in and custom stream encryption |
| AudioMixing | Examples/Advanced/AudioMixing/ |
startAudioMixing(), stopAudioMixing(), adjustAudioMixingVolume(), setEffectsVolume() |
Mix local audio file with microphone input |
| PrecallTest | Examples/Advanced/PrecallTest/ |
startEchoTest(), stopEchoTest(), startLastmileProbeTest() |
Pre-call echo test and last-mile network probe |
| MediaPlayer | Examples/Advanced/MediaPlayer/ |
createMediaPlayer(), updateChannelEx() |
Play media files and publish to channel via media player |
| ScreenShare | Examples/Advanced/ScreenShare/ |
startScreenCapture(), updateScreenCapture(), stopScreenCapture() |
Screen capture and sharing via ReplayKit extension |
| LocalVideoTranscoding | Examples/Advanced/LocalVideoTranscoding/ |
startLocalVideoTranscoder(), startCameraCapture(), createMediaPlayer() |
Transcode multiple video sources locally before publishing |
| LocalVideoComposition | Examples/Advanced/LocalVideoComposition/ |
startLocalVideoTranscoder(), startCameraCapture(), startScreenCapture() |
Composite camera and screen capture into one stream |
| VideoProcess | Examples/Advanced/VideoProcess/ |
setBeautyEffectOptions(), enableVirtualBackground(), enableExtension() |
Built-in beauty, virtual background, and video enhancement |
| RhythmPlayer (hidden) | Examples/Advanced/RhythmPlayer/ |
startRhythmPlayer(), stopRhythmPlayer() |
Source retained for reference; hidden because the APIs are deprecated since RTC SDK 4.6.0 |
| CreateDataStream | Examples/Advanced/CreateDataStream/ |
createDataStream(), sendStreamMessage() |
Create and send data stream messages between users |
| MediaChannelRelay | Examples/Advanced/MediaChannelRelay/ |
startOrUpdateChannelMediaRelay(), stopChannelMediaRelay(), pauseAllChannelMediaRelay() |
Relay media stream to multiple destination channels |
| SpatialAudio | Examples/Advanced/SpatialAudio/ |
createMediaPlayer(), updateChannel() |
3D spatial audio with media player integration |
| ContentInspect | Examples/Advanced/ContentInspect/ |
enableContentInspect(), switchCamera() |
Moderate content in video stream |
| MutliCamera | Examples/Advanced/MutliCamera/ |
enableMultiCamera(), startCameraCapture(), stopCameraCapture() |
Capture from front and back cameras simultaneously (iOS 13+) |
| KtvCopyrightMusic | Examples/Advanced/KtvCopyrightMusic/ |
— | Links to KTV copyright music documentation |
| ARKit | Examples/Advanced/ARKit/ |
setVideoFrameDelegate(), enableInstantMediaRendering(), startMediaRenderingTracing() |
Push ARKit face tracking frames as custom video source |
| AudioWaveform | Examples/Advanced/AudioWaveform/ |
setAudioProfile(), enableAudioVolumeIndication() |
Visualize audio waveform from volume callbacks |
| Simulcast | Examples/Advanced/Simulcast/ |
setSimulcastConfig(), setRemoteVideoStream() |
Publish multiple video quality layers simultaneously |
| Multipath | Examples/Advanced/Multipath/ |
updateChannel() |
Multi-path network transmission configuration |
APIExample-SwiftUI/
├── Podfile # CocoaPods dependencies (AgoraRtcEngine_iOS)
├── Agora-ScreenShare-Extension/ # ReplayKit broadcast extension for screen sharing
├── libs/ # Local SDK frameworks (when not using CocoaPods)
└── APIExample-SwiftUI/
├── APIExample_SwiftUIApp.swift # App entry point (@main)
├── ContentView.swift # Root navigation — MenuItem registration lives here
├── Info.plist
├── APIExample-Bridging-Header.h
│
├── Common/
│ ├── KeyCenter.swift # App ID and Certificate
│ ├── AgoraExtension.swift
│ ├── PickerView.swift
│ ├── StatisticsInfo.swift
│ ├── VideoView.swift # SwiftUI wrapper for video rendering
│ ├── VideoUIView.swift # UIKit video view
│ ├── ViewExtensions.swift
│ ├── View/ # Reusable SwiftUI components
│ ├── Settings/ # GlobalSettings
│ ├── Utils/ # LogUtils, Util (privatization config)
│ ├── NetworkManager/ # Token request helper
│ ├── ExternalAudio/ # External audio source helpers
│ ├── ExternalVideo/ # External video source helpers
│ ├── CustomEncryption/ # Custom stream encryption helpers
│ └── ARKit/ # ARKit integration helpers
│
├── Examples/
│ ├── Basic/
│ │ ├── JoinChannelVideo/ # "Join a channel (Video)"
│ │ ├── JoinChannelVideo(Token)/ # "Join a channel (Token)"
│ │ ├── JoinChannelVideo(Recorder)/ # "Local or remote recording"
│ │ └── JoinChannelAudio/ # "Join a channel (Audio)"
│ └── Advanced/
│ ├── LiveStreaming/ # "Live Streaming"
│ ├── RTMPStream/ # "RTMP Streaming"
│ ├── VideoMetadata/ # "Media Metadata"
│ ├── VoiceChanger/ # "Voice Effects"
│ ├── CustomPCMAudioSource/ # "Custom Audio Source (PCM)"
│ ├── CustomAudioRender/ # "Custom Audio Render"
│ ├── RawAudioData/ # "Raw Audio Data"
│ ├── RawVideoData/ # "Raw Video Data"
│ ├── PictureInPicture/ # "Picture In Picture"
│ ├── QuickSwitchChannel/ # "Quick Switch Channel"
│ ├── JoinMultiChannel/ # "Join Multiple Channels"
│ ├── StreamEncryption/ # "Stream Encryption"
│ ├── AudioMixing/ # "Audio Mixing"
│ ├── PrecallTest/ # "Pre-call Test"
│ ├── MediaPlayer/ # "Media Player"
│ ├── ScreenShare/ # "Screen Share"
│ ├── LocalVideoTranscoding/ # "Local Video Transcoding"
│ ├── LocalVideoComposition/ # "Local Composite Graph"
│ ├── VideoProcess/ # "Video Process"
│ ├── RhythmPlayer/ # Hidden — APIs deprecated since RTC SDK 4.6.0
│ ├── CreateDataStream/ # "Send Data Stream"
│ ├── MediaChannelRelay/ # "Media Channel Relay"
│ ├── SpatialAudio/ # "Spatial Audio"
│ ├── ContentInspect/ # "Content Inspect"
│ ├── MutliCamera/ # "Multi Camera"
│ ├── KtvCopyrightMusic/ # "KTV Copyright Music"
│ ├── ARKit/ # "ARKit"
│ ├── AudioWaveform/ # "Audio Waveform"
│ ├── Simulcast/ # "Simulcast"
│ └── Multipath/ # "Multipath"
│
├── Resources/ # Audio/video sample files and en/zh-Hans localizations
├── Assets.xcassets/
└── Preview Content/
Registration is manual via the menus array in ContentView.swift. No reflection or annotation scanning.
MenuItem struct:
struct MenuItem: Identifiable {
let id = UUID()
var name: String
var view: AnyView // the Entry view wrapped in AnyView
}Navigation uses SwiftUI NavigationLink. Each MenuItem holds an AnyView wrapping the Entry view.
To add a case, update implementation, registration, and Xcode target membership:
- Add a
MenuItemto themenusarray inContentView.swift:MenuItem(name: "My New Case".localized, view: AnyView(MyNewCaseEntry()))
- Create the example folder under
Examples/Basic/orExamples/Advanced/with the Swift files - Add the new Swift files to the
APIExample-SwiftUItarget's Sources build phase inAPIExample-SwiftUI.xcodeproj/project.pbxproj; add any new assets or localized files to its Resources build phase
Every example is split into two parts:
Entry (<ExampleName>Entry : View)
- A SwiftUI View that collects user configuration (channel name, etc.)
- Uses
NavigationLinkto navigate to the main view - Passes configuration via a
configsdictionary
RTC (<ExampleName>RTC : NSObject, ObservableObject, AgoraRtcEngineDelegate)
- Owns the
AgoraRtcEngineKitlifecycle - Exposes state to the View via
@Publishedproperties - Implements all delegate callbacks
Main View (<ExampleName> : View)
- Uses
@StateObjectwhen it creates and owns the RTC object - Uses
@ObservedObjectonly when a stable owner creates and injects the RTC object - Calls
setupRTC()in.onAppear - Calls
onDestroy()in.onDisappear
UIKit video views (VideoUIView) are bridged into SwiftUI via UIViewRepresentable (VideoView). The RTC class owns the UIView instances; the SwiftUI View wraps them for display.
.onAppear → setupRTC()
→ AgoraRtcEngineKit.sharedEngine(with:delegate:)
→ engine.setVideoEncoderConfiguration / setClientRole
→ engine.joinChannel() (after token generation)
↓
[AgoraRtcEngineDelegate callbacks]
↓
.onDisappear → onDestroy()
→ engine.leaveChannel()
→ AgoraRtcEngineKit.destroy()
Use the guarded permission → Token → join implementation in upsert-case. Snapshot channel, UID, request generation and engine identity before asynchronous work. Recheck them on main before each continuation; leave/destroy invalidates pending requests and destroy clears engine ownership. Weak capture alone does not protect a still-alive controller whose RTC session has ended.
A nil/empty Token is allowed only when no App Certificate is configured. Reject a missing required Token and check the SDK join return code without logging credentials.