iOS/APIExample-SwiftUI/.agent/skills/upsert-case/SKILL.md
Add a new API demo case or modify an existing one in the APIExample-SwiftUI project. Covers folder creation, Entry view, RTC class, MenuItem registration, and Case Index update.
npx skillsauth add agoraio/api-examples upsert-caseInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Examples/Basic/ or Examples/Advanced/Before adding, search the Case Index in ARCHITECTURE.md to confirm the case does not already exist.
| Scenario | Files |
|----------|-------|
| Add new case | New folder + <ExampleName>RTC.swift + <ExampleName>.swift, ContentView.swift (MenuItem), ARCHITECTURE.md (Case Index) |
| Modify existing case | Existing *RTC.swift and/or *.swift view files, ARCHITECTURE.md (Case Index) |
APIExample-SwiftUI/Examples/[Basic|Advanced]/<ExampleName>/
Create <ExampleName>RTC.swift — owns the engine lifecycle:
import AgoraRtcKit
import SwiftUI
class <ExampleName>RTC: NSObject, ObservableObject {
var agoraKit: AgoraRtcEngineKit!
private var isJoined = false
func setupRTC(configs: [String: Any]) {
let config = AgoraRtcEngineConfig()
config.appId = KeyCenter.AppId
agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self)
guard let channelName = configs["channelName"] as? String else { return }
let option = AgoraRtcChannelMediaOptions()
option.clientRoleType = .broadcaster
NetworkManager.shared.generateToken(channelName: channelName) { [weak self] token in
self?.agoraKit.joinChannel(byToken: token, channelId: channelName,
uid: 0, mediaOptions: option)
}
}
func onDestroy() {
if isJoined { agoraKit.leaveChannel(nil) }
AgoraRtcEngineKit.destroy()
}
}
extension <ExampleName>RTC: AgoraRtcEngineDelegate {
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String,
withUid uid: UInt, elapsed: Int) {
isJoined = true
LogUtils.log(message: "Joined: \(channel)", level: .info)
}
func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurError errorCode: AgoraErrorCode) {
LogUtils.log(message: "Error: \(errorCode)", level: .error)
}
}
Create <ExampleName>.swift with Entry and Main views:
import SwiftUI
struct <ExampleName>Entry: View {
@State private var channelName = ""
@State private var isActive = false
@State private var configs: [String: Any] = [:]
var body: some View {
VStack {
TextField("Enter channel name".localized, text: $channelName)
.textFieldStyle(.roundedBorder).padding()
Button("Join".localized) {
configs = ["channelName": channelName]
isActive = true
}.disabled(channelName.isEmpty)
NavigationLink(destination: <ExampleName>(configs: configs),
isActive: $isActive) { EmptyView() }
}
}
}
struct <ExampleName>: View {
@State var configs: [String: Any] = [:]
@ObservedObject private var rtc = <ExampleName>RTC()
var body: some View {
VStack { /* UI here */ }
.onAppear { rtc.setupRTC(configs: configs) }
.onDisappear { rtc.onDestroy() }
}
}
Add to the menus array in APIExample-SwiftUI/ContentView.swift:
MenuItem(name: "<Display Name>".localized, view: AnyView(<ExampleName>Entry()))
Add a row to the ## Case Index table in ARCHITECTURE.md:
| <ExampleName> | `Examples/[Basic|Advanced]/<ExampleName>/` | `keyApi1()`, `keyApi2()` | One-line description |
NSObject, conforms to ObservableObject and AgoraRtcEngineDelegatesetupRTC, destroyed in onDestroy@ObservedObject (not @StateObject) for the RTC objectsetupRTC called in .onAppear, onDestroy called in .onDisappearleaveChannel + AgoraRtcEngineKit.destroy() called in onDestroyDispatchQueue.mainContentView.swiftARCHITECTURE.mdAgoraRtcEngineKit in the Entry view@StateObject for the RTC object in the Main view — the Main view does not own its lifetimebody — only in .onAppear, .onDisappear, or explicit user action handlersAgoraRtcEngineDelegate callbacks — always DispatchQueue.main.async { }AgoraRtcEngineKit instance between casesjoinChannel before requesting camera/microphone permissionsARCHITECTURE.mddevelopment
Add a new API example or modify an existing one. Covers both creation and modification scenarios, including dialog class structure, message map registration, and ARCHITECTURE.md updates.
development
Code review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly.
development
Add a new API example or modify an existing one. Covers both creation and modification scenarios, including file structure, registration, and ARCHITECTURE.md updates.
development
Code review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly.