APIExample-Audio/
├── Podfile # CocoaPods dependencies (AgoraAudio_iOS, Floaty, AGEVideoLayout)
└── APIExample-Audio/
├── AppDelegate.swift
├── ViewController.swift # Root menu controller — MenuItem registration lives here
├── Info.plist
├── APIExample.entitlements
├── APIExample-Bridging-Header.h
│
├── Common/
│ ├── KeyCenter.swift # App ID and Certificate
│ ├── GlobalSettings.swift # Shared runtime config
│ ├── BaseViewController.swift # Base class all Main VCs must extend
│ ├── EntryViewController.swift # Generic Entry VC for storyboard == "Main" cases
│ ├── LogViewController.swift # Log viewer
│ ├── AlertManager.swift
│ ├── AgoraExtension.swift
│ ├── StatisticsInfo.swift
│ ├── UITypeAlias.swift
│ ├── VideoView.swift / .xib # Audio seat view (no video rendering)
│ ├── Settings/ # Settings UI components
│ ├── Utils/ # LogUtils, Util (privatization config)
│ ├── NetworkManager/ # Token request helper
│ ├── ExternalAudio/ # External audio source helpers
│ └── ExternalVideo/ # (unused in audio project)
│
├── Examples/
│ ├── Basic/
│ │ ├── JoinChannelAudio/ # "Join a channel (Audio)"
│ │ └── JoinChannelAudio(Token)/ # "Join a channel (Token)"
│ └── Advanced/
│ ├── VoiceChanger/ # "Voice Changer" — voice beautifier/effects
│ ├── CustomAudioSource/ # "Custom Audio Source"
│ ├── CustomPcmAudioSource/ # "Custom Audio Source (PCM)"
│ ├── CustomAudioRender/ # "Custom Audio Render"
│ ├── RawAudioData/ # "Raw Audio Data"
│ ├── AudioMixing/ # "Audio Mixing"
│ ├── RhythmPlayer/ # "Rhythm Player"
│ ├── PrecallTest/ # "Precall Test"
│ └── SpatialAudio/ # "Spatial Audio"
│
├── Resources/ # Audio sample files
├── Assets.xcassets/
├── Base.lproj/ # Main.storyboard, LaunchScreen.storyboard
└── zh-Hans.lproj/ # Chinese localization
Registration is manual via the menus array in ViewController.swift. Identical to APIExample.
MenuItem struct:
struct MenuItem {
var name: String // display name in the list
var entry: String // storyboard ID of the entry VC (default: "EntryViewController")
var storyboard: String // storyboard file name (default: "Main")
var controller: String // storyboard ID of the main VC
var note: String // optional description
}To add a case, edit exactly two things:
- Add a
MenuItemto themenusarray inViewController.swift - Create the example folder under
Examples/Basic/orExamples/Advanced/with the Swift file(s) and storyboard
Identical to APIExample:
Entry (<ExampleName>Entry : UIViewController)
- Collects user configuration before entering the example
- Passes configuration to Main via a
configsdictionary
Main (<ExampleName>Main : BaseViewController)
- Owns the
AgoraRtcEngineKitlifecycle for the duration of the example - Implements
AgoraRtcEngineDelegate - Receives configuration exclusively through
configs - UI contains only audio controls — no video rendering views
This project uses AgoraAudio_iOS SDK which has no video module. Main view controllers must NOT include:
- Video rendering views or video canvas setup
- Calls to
enableVideo(),setupLocalVideo(),setupRemoteVideo() - Camera-related APIs
All UI is limited to audio controls, status indicators, and effect parameter inputs.
viewDidLoad → AgoraRtcEngineKit.sharedEngine(withAppId:delegate:)
→ engine.setAudioProfile / setAudioScenario
→ engine.joinChannel() (after RECORD_AUDIO permission granted)
↓
[AgoraRtcEngineDelegate callbacks — may be on background thread]
↓
viewDidDisappear / willMove(toParent:)
→ engine.leaveChannel()
→ AgoraRtcEngineKit.destroy()
NetworkManager.shared.generateToken(channelName: channelId, uid: uid) { token in
self.agoraKit?.joinChannel(byToken: token, channelId: channelId, uid: uid, mediaOptions: options)
}