Native iOS RTC integration
The iOS SDK uses native audio and video capture and rendering. Your SwiftUI/UIKit host owns the layout and interactions; the SDK provides meeting state, media, and replay resources. Start with receive-only on a real iPhone or iPad, then enable camera and microphone separately.
flowchart LR
A["Page retains SDK and delegate"] --> B["Join and wait for didJoin"]
B --> C["Bind each remote track"]
C --> D["Check media after backgrounding"]
D --> E["Leave and release views"]
Import a matching SDK build
Use the matching AKStreamRtcSDK Swift Package, or embed its AKStreamRtcSDK/WebRTC XCFrameworks and resources together. The minimum is iOS 15. Set meaningful camera and microphone usage descriptions in Info.plist. Cross-app screen sharing separately needs a ReplayKit Broadcast Extension, App Groups, and matching signing. A test CA belongs only to an isolated lab; a production app must validate the real certificate chain and hostname.
For source integration, AKStreamRtcSDK/Package.swift refers to a sibling WebRTCSDK by relative path. Keep both directories together and choose the AKStreamRtcSDK product in Xcode. Copying only one Package.swift loses its WebRTC dependency. For binary integration, embed both matching XCFrameworks and verify that the target architecture fits the device or simulator. Use NSCameraUsageDescription and NSMicrophoneUsageDescription for real business-purpose text.
Create and strongly retain the client and delegate from a @MainActor page:
let client = AkNativeRtcClient(localRenderer: localRenderer, delegate: delegate)
client.setInitialMediaEnabled(microphone: false, camera: false)
client.joinWithAccessTicket(baseURL: serverURL, roomID: roomID,
accessTicket: accessTicket, passcode: passcode)
localRenderer implements RTCVideoRenderer; delegate implements AkNativeRtcClientDelegate. Wait for didJoin or didFail. Bind each didReceiveRemoteVideo track to its own view and remove only that publication when it ends. The OS camera indicator does not prove microphone packets are being sent.
| Delegate callback | Host response |
|---|---|
didJoin, didFail |
Finish or fail join waiting; do not infer success from a synchronous method return |
didReceiveRemoteVideo, didRemoveRemoteVideo |
Create/remove a view by publicationID; do not merge camera and screen from one member |
didChangeLocalMedia, didChangeMediaPermission |
Show actual local inputs and host restrictions; lifting a restriction does not unmute |
didChangeContinuousRecording |
Show current run or none |
didFailHTTP |
Keep the HTTP status to distinguish an expired ticket from missing permission |
Permissions, recording, and exit
Drive controls from didChangeLocalMedia, didChangeMediaPermission, and errors. Turning off the camera should not mute the microphone. After foreground recovery, inspect actual media state. Let the SDK coordinate audio routing instead of starting conflicting AVAudioSessions per video view.
In the matching version, try await client.getContinuousRecording() queries active recording. An authorized host uses startContinuousRecording() and stopContinuousRecording(recordingId:); didChangeContinuousRecording reports state. Your app renders recording and replay UI. An old 1.0.0.157 app lacks the newer archive abilities. See iOS methods for Swift inputs, callbacks, and ReplayKit edge cases.
A successful control request may first return Starting or Stopping. Keep reading the callback or querying until the run reaches its actual state. After host transfer, the previous host's control should be rejected. Post-meeting archives require a new ticket from your business backend, never a reused admission or cached RTC token.
Call client.leave() and follow SDK disposal when departing; hiding the page alone is insufficient. Keep signing, developer trust, and certificate problems separate from media failures. Follow the acceptance checklist.
For “invalid certificate,” compare the actual server address with certificate SAN, chain, and device time, then inspect the app's trust configuration. Installing a lab CA is not proof the app trusts it. If local preview works but the server has no iPhone microphone stream, check capture and sent-packet counts, OS audio route, and another device's receive result. A green camera indicator alone is not proof of sound.
| Symptom | Check first | Evidence of resolution |
|---|---|---|
| App says developer is untrusted | Profile expiry, device trust, signing | Latest build of the same bundle ID launches from the home screen |
| Join reconnects forever | LAN address, HTTPS, WebSocket, ticket | didJoin arrives and server sees this device session |
| Local camera works but others see nothing | Camera send stats, WHIP/ICE | Second device continuously decodes new frames |
| Camera indicator is on but others hear nothing | Microphone permission, route, sent packets | Other device receives packets and real sound |
| Screen share unavailable | ReplayKit extension and App Groups signing | System broadcast picker finds extension and creates a share |