iOS SDK Guide
Install, join a channel, publish/subscribe, screen sharing and permission notes.
The iOS surface is a native engine with Objective-C selector shapes: an AgoraRtcEngineKit singleton plus AgoraRtcEngineDelegate callbacks. Method names match the frozen Agora iOS SDK reference version symbol by symbol (the SDK interface definition; for apiVersion see "Platform and public interface definition" on this page).
Prerequisites
- Xcode 16+, an iOS 15+ deployment target, Swift 5.9+ (Objective-C is supported too).
Info.plistmust containNSCameraUsageDescriptionandNSMicrophoneUsageDescription, otherwise the system rejects capture outright (with no prompt).- Screen sharing needs a separate Broadcast Upload Extension (ReplayKit) with an App Group configured.
- You already have an
appIdand a token service (see Enable services and get credentials).
Install
# Podfile (planned coordinate; not yet published to the public CocoaPods codebase)
pod 'NimeetRtc', :path => '../sdk/ios'Swift Package and Carthage forms will be published together in the release notes.
Get a token
The client must not hold customerSecret. The recommended setup has the host app's own backend issue tokens, injected through the callback form of RtcTokenProvider (the same concept as on Android):
let provider = FallbackTokenProvider(providers: [
CallbackTokenProvider { channel, uid, role, completion in
authClient.fetchRtcToken(channel: channel, uid: uid, role: role, completion: completion)
},
StaticTokenProvider(token: ProcessInfo.processInfo.environment["NIMEET_TEST_TOKEN"] ?? "")
])Join a channel
import NimeetRtc
final class MeetingController: NSObject, AgoraRtcEngineDelegate {
private var engine: AgoraRtcEngineKit?
func start(appId: String, token: String, localView: UIView) {
let config = AgoraRtcEngineConfig()
config.appId = appId
engine = AgoraRtcEngineKit.sharedEngine(withAppId: appId, delegate: self)
engine?.setChannelProfile(.liveBroadcasting)
engine?.enableVideo()
let canvas = AgoraRtcVideoCanvas()
canvas.view = localView
engine?.setupLocalVideo(canvas)
engine?.startPreview()
let options = AgoraRtcChannelMediaOptions()
options.clientRoleType = .broadcaster
engine?.joinChannel(byToken: token,
channelId: "weekly-sync",
uid: 10001,
mediaOptions: options,
joinSuccess: { channel, uid, elapsed in
print("joined \(channel) uid=\(uid) elapsed=\(elapsed)")
})
}
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) {
// A remote user joined: allocate the remote video container
}
func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {
// A remote user left
}
func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurError errorCode: AgoraErrorCode) {
// Route by error number; see the error handling section
}
}Publish and subscribe
The native surface has no publish(track): AgoraRtcChannelMediaOptions decides what is published when joining, and switches control it afterwards.
engine?.muteLocalAudioStream(true) // mute the microphone
engine?.muteLocalVideoStream(true) // turn off the camera video
engine?.updateChannelMediaOptions(options) // switch roles / change the subscription scopeScreen sharing uses ReplayKit: the host app starts a Broadcast Upload Extension, and the extension calls startScreenCapture: to feed sample buffers to the SDK.
Events
| Callback | When it fires | Typical handling |
|---|---|---|
rtcEngine:didJoinedOfUid:elapsed: | A remote user joined | Allocate a container |
rtcEngine:firstRemoteVideoDecodedOfUid:size:elapsed: | The first remote frame was decoded | Render |
rtcEngine:didOfflineOfUid:reason: | A remote user left | Release the container |
rtcEngine:didOccurError: | A runtime error | Route by AgoraErrorCode |
| Token will-expire callback | Before the privilege expires | Renew, then renewToken: |
Error handling
- Synchronous methods return
Int32:0means success, and a negative value is an Agora error number. - Asynchronous errors go through
didOccurError. - Unimplemented capabilities return
-4(ERR_NOT_SUPPORTED) and fail explicitly.
Troubleshooting
| Symptom | Cause | Handling |
|---|---|---|
| Turning on the camera does nothing | A usage description is missing from Info.plist | Add NSCameraUsageDescription / NSMicrophoneUsageDescription |
| Black screen on a physical device but fine in the simulator | The simulator has no camera | Verify the capture path on a physical device |
| Joining fails | The token does not match the appId/channel | Self-check with GET /v1/tokens/verify |
| Screen sharing shows no video | The extension has no App Group configured | Check the extension's NSExtension configuration and the shared container |
| Media stops in the background | iOS suspends the app | Use ReplayKit, or a foreground audio-session keep-alive strategy |
Next steps
- Full interfaces and overloads: API reference for this platform
- Error codes and troubleshooting: Error code table, FAQ
- Issue tokens on your server: Token authentication
Platform information
| Item | value |
|---|---|
| Platform | iOS |
| Language | Swift / Objective-C |
| SDK package | NimeetRtc |
| Interface version | 4.7.0 |
| Compatible Agora API | Agora ios API |
The public interfaces are aligned with the Agora ios API; installation and runtime requirements are listed under Prerequisites above.
Availability
This platform's SDK: Generally available. The examples use the public API; runtime capability follows the published SDK package.
join-channel flow
The method names, parameter types and return values match the API reference for the same platform. Where a method has several overloads, the most common one is shown here; see the API reference for all overloads.
| Step | Interface | Method | Overload count |
|---|---|---|---|
| enable video and local preview | AgoraRtcEngineKit | enableVideo | 1 |
| Join a channel | AgoraRtcEngineKit | joinChannelByToken:channelId:info:uid:joinSuccess: | 1 |
| Leave a channel | AgoraRtcEngineKit | leaveChannel: | 1 |
| Create instance / initialize | AgoraRtcEngineKit | sharedEngineWithAppId:delegate: | 1 |
enableVideo(): int
joinChannelByToken:channelId:info:uid:joinSuccess:(token?: NSString*, channelId: NSString*, info?: NSString*, uid: NSUInteger, joinSuccessBlock?: void(^ )(NSString*channel,NSUInteger uid,NSInteger elapsed)): int
leaveChannel:(leaveChannelBlock?: void(^ )(AgoraChannelStats*stat)): int
sharedEngineWithAppId:delegate:(appId: NSString*, delegate?: id): instancetypeEvents and callbacks
This SDK has no event callbacks.
Error codes
| Constant | value |
|---|---|
AgoraErrorCodeAdmGeneralError | 1005 |
AgoraErrorCodeAdmInitLoopback | 1022 |
AgoraErrorCodeAdmInitPlayout | 1008 |
AgoraErrorCodeAdmInitRecording | 1011 |
AgoraErrorCodeAdmJavaResource | 1006 |
AgoraErrorCodeAdmNoPlayoutDevice | 1360 |
AgoraErrorCodeAdmNoRecordingDevice | 1359 |
AgoraErrorCodeAdmPlayAbnormalFrequency | 1020 |
AgoraErrorCodeAdmRecordAbnormalFrequency | 1021 |
AgoraErrorCodeAdmRecordAudioFailed | 1018 |
AgoraErrorCodeAdmRuntimePlayoutError | 1015 |
AgoraErrorCodeAdmRuntimeRecordingError | 1017 |
AgoraErrorCodeAdmSampleRate | 1007 |
AgoraErrorCodeAdmStartLoopback | 1023 |
AgoraErrorCodeAdmStartPlayout | 1009 |
AgoraErrorCodeAdmStartRecording | 1012 |
AgoraErrorCodeAdmStopPlayout | 1010 |
AgoraErrorCodeAdmStopRecording | 1013 |
AgoraErrorCodeAlreadyInUse | 19 |
AgoraErrorCodeBitrateLimit | 115 |
AgoraErrorCodeBufferTooSmall | 6 |
AgoraErrorCodeConnectionInterrupted | 111 |
AgoraErrorCodeConnectionLost | 112 |
AgoraErrorCodeDecryptionFailed | 120 |
AgoraErrorCodeEncryptedStreamNotAllowedPublish | 130 |
AgoraErrorCodeFailed | 1 |
AgoraErrorCodeInitNetEngine | 21 |
AgoraErrorCodeInvalidAppId | 101 |
AgoraErrorCodeInvalidArgument | 2 |
AgoraErrorCodeInvalidChannelId | 102 |
AgoraErrorCodeInvalidState | 8 |
AgoraErrorCodeInvalidToken | 110 |
AgoraErrorCodeInvalidUserAccount | 134 |
AgoraErrorCodeJoinChannelRejected | 17 |
AgoraErrorCodeLeaveChannelRejected | 18 |
AgoraErrorCodeLoadMediaEngine | 1001 |
AgoraErrorCodeNoError | 0 |
AgoraErrorCodeNoServerResources | 103 |
AgoraErrorCodeNotInChannel | 113 |
AgoraErrorCodeNotInitialized | 7 |
AgoraErrorCodeNotReady | 3 |
AgoraErrorCodeNotSupported | 4 |
AgoraErrorCodePcmSendBufferOverflow | 201 |
AgoraErrorCodePcmSendFormat | 200 |
AgoraErrorCodeResourceLimited | 22 |
AgoraErrorCodeSizeTooLarge | 114 |
AgoraErrorCodeStartCall | 1002 |
AgoraErrorCodeStartCamera | 1003 |
AgoraErrorCodeStartVideoRender | 1004 |
AgoraErrorCodeTimedOut | 10 |
AgoraErrorCodeTokenExpired | 109 |
AgoraErrorCodeTooManyDataStreams | 116 |
AgoraErrorCodeVcmEncoderEncodeError | 1602 |
AgoraErrorCodeVcmEncoderInitError | 1601 |
AgoraErrorCodeVcmEncoderSetError | 1603 |
AgoraErrorCodeVcmUnknownError | 1600 |
AgoraErrorCodeVdmCameraNotAuthorized | 1501 |
AgoraErrorCodeVideoEffectAssetInvalid | 1700 |
AgoraErrorCodeVideoEffectEngineInvalid | 1702 |
AgoraErrorCodeVideoEffectInvalidBundlePath | 1707 |
AgoraErrorCodeVideoEffectInvalidParam | 1705 |
AgoraErrorCodeVideoEffectNodeNotActive | 1704 |
AgoraErrorCodeVideoEffectNotSupported | 1706 |
AgoraErrorCodeVideoEffectSaveFailed | 1701 |
AgoraErrorCodeWatermarkAGRB | 128 |
AgoraErrorCodeWatermarkInfo | 127 |
AgoraErrorCodeWatermarkParam | 124 |
AgoraErrorCodeWatermarkPath | 125 |
AgoraErrorCodeWatermarkPng | 126 |
AgoraErrorCodeWatermarkRead | 129 |
the platform REST layer uses REST error codes;the SDK layer maps connection/authentication failures to Agora error numbers, for the mapping table see SDK error identifiers per platform。