Skip to content

Latest commit

 

History

History
194 lines (171 loc) · 13.6 KB

File metadata and controls

194 lines (171 loc) · 13.6 KB

ARCHITECTURE.md — APIExample-SwiftUI

Application Lifecycle

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 Index

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

Directory Layout

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/

Case Registration Mechanism

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:

  1. Add a MenuItem to the menus array in ContentView.swift:
    MenuItem(name: "My New Case".localized, view: AnyView(MyNewCaseEntry()))
  2. Create the example folder under Examples/Basic/ or Examples/Advanced/ with the Swift files
  3. Add the new Swift files to the APIExample-SwiftUI target's Sources build phase in APIExample-SwiftUI.xcodeproj/project.pbxproj; add any new assets or localized files to its Resources build phase

Entry/RTC Pattern

Every example is split into two parts:

Entry (<ExampleName>Entry : View)

  • A SwiftUI View that collects user configuration (channel name, etc.)
  • Uses NavigationLink to navigate to the main view
  • Passes configuration via a configs dictionary

RTC (<ExampleName>RTC : NSObject, ObservableObject, AgoraRtcEngineDelegate)

  • Owns the AgoraRtcEngineKit lifecycle
  • Exposes state to the View via @Published properties
  • Implements all delegate callbacks

Main View (<ExampleName> : View)

  • Uses @StateObject when it creates and owns the RTC object
  • Uses @ObservedObject only when a stable owner creates and injects the RTC object
  • Calls setupRTC() in .onAppear
  • Calls onDestroy() in .onDisappear

Video Rendering

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.

AgoraRtcEngineKit Lifecycle

.onAppear      → setupRTC()
               → AgoraRtcEngineKit.sharedEngine(with:delegate:)
               → engine.setVideoEncoderConfiguration / setClientRole
               → engine.joinChannel() (after token generation)
                      ↓
                 [AgoraRtcEngineDelegate callbacks]
                      ↓
.onDisappear   → onDestroy()
               → engine.leaveChannel()
               → AgoraRtcEngineKit.destroy()

Token Flow

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.