Flex SDK for iOS: More options
After you add the Flex SDK for iOS to your app using the Quickstart, you can use the options on this page to further customize your integration.
Use a secret API key
Aircore uses two types of API keys: publishable and secret. See Authentication for an overview of each.
The Quickstart uses a publishable API key for simplicity. This section explains how to use a secret API key instead for better security.
When you use a secret API key, your backend gets session auth tokens from the Aircore provisioning service. You use these tokens when you create channels. Tokens expire, so you have to keep updating them.
Using a secret API key includes three main tasks:
Request and use a session auth token when you create a channel.
Update the token before it expires to keep using the channel.
Reconnect to the channel if your app uses an expired token.
Create a channel
Request a session auth token and use this syntax for createChannel()
:
let channel = Engine.sharedInstance.createChannel(sessionAuthToken: TOKEN)
The token determines whether the user can publish audio.
Update tokens before they expire
The sessionAuthTokenNearingExpiry
notification tells you when a token is about to expire.
Replace the token to continue using the channel:
let channel: Channel
NotificationCenter.default.addObserver(self,
selector: #selector(onSessionAuthTokenNearingExpiry(_:)),
name: ChannelNotification.sessionAuthTokenNearingExpiry, object: channel)
@objc func onSessionAuthTokenNearingExpiry(_ notif: NSNotification) {
// Get a new token from your backend and provide it to the channel
getNewSessionAuthTokenFromBackend() { newToken in
channel.updateSessionAuthToken(newToken)
}
}
You can update the token at any time, except when the channel's join state is .terminated
.
You can also change the permissions by updating the token. For example, you can have a user without permission to publish audio join a channel. To give the user permission later, you can provide a new token.
Handle expired tokens
Avoid letting tokens expire by listening for the sessionAuthTokenNearingExpiry
notification.
If your app tries to join a channel with an expired token:
The channel disconnects.
The
joinState
is.terminated
.The
terminationCause
issessionAuthTokenInvalid
.
You can use the join state notification to handle this issue. See Track the status of a channel.
var sessionAuthToken = "some initial session auth token from your backend"
var channel: Channel?
func createChannel(_ newToken: String) {
sessionAuthToken = newToken
channel = Engine.sharedInstance.createChannel(sessionAuthToken: sessionAuthToken)
NotificationCenter.default.addObserver(self, #selector(onJoinStateDidChange(_:), ChannelNotification.joinStateDidChange, object: channel)
}
@objc func onJoinStateDidChange(_ notification: Notification) {
guard let newState = notification.userInfo?[ChannelNotification.Key.newJoinState] as? Channel.JoinState
else { return }
if newState == .terminated &&
channel?.terminationCause == .sessionAccessTokenInvalid {
// Get a new token from your backend and create a new Channel
getNewSessionAuthTokenFromBackend() { [weak self] newToken in
self?.createChannel(newToken)
}
}
}
Use channel join states with your UI
This diagram shows the possible changes in join state for a channel:
To build your UI, decide which actions to take in each join state:
.notJoined |
|
.joined and .rejoining |
|
.terminated |
|
To terminate a channel, call the leave()
method at any time, except when the join state is already .terminated
.
For a full implementation of the join state notification, see these files in our sample app:
Create a muted stream
You can create a muted stream by setting initialAudioMute
to true
:
import AircoreMedia
// ... Create and join a channel
// Make sure the channel is connected
let channelJoinState = channel.joinState
guard channelJoinState == .joined || channelJoinState == .rejoining else { return }
// Optional parameters
let params = LocalStreamParams()
params.initialAudioMute = true
let localStream = channel.createLocalStream(params: params)
guard let localStream = localStream else { fatalError() } // Handle error case
Add metadata to a local stream
Your app can include metadata with a local stream. For example, you may want to include a user-specified name that other clients can display in the UI.
The metadata is permanent for the local stream, and members of the channel can read it when they receive a RemoteStream
object.
The StreamMetadata
object contains key-value pairs, where the keys are strings and the values are simple types (string, number, boolean, or null). To attach it to a stream, add it to the metadata
parameter of the LocalStreamParams
for initialization.
Get notifications for added and removed streams
All remote streams for a channel are listed in its remoteStreams
property. Your app can receive notifications when streams are added or removed:
import AircoreMedia
let channel: Channel
NotificationCenter.default.addObserver(self,
selector: #selector(onRemoteStreamAdded(_:)),
name: ChannelNotification.remoteStreamWasAdded, object: channel)
NotificationCenter.default.addObserver(self,
selector: #selector(onRemoteStreamRemoved(_:)),
name: ChannelNotification.remoteStreamWasRemoved, object: channel)
For these notifications, the object
is the relevant channel and the userInfo
dictionary references the added or removed stream.
@objc func onRemoteStreamAdded(_ notif: NSNotification) {
// You can choose to keep the RemoteStream. Otherwise, you can access it
// from the channel's remoteStreams property
let remoteStream = notif.userInfo?[ChannelNotification.Key.remoteStream] as?
RemoteStream }
Get notifications for remote stream connection status
You can receive notifications when the connection status for a remote stream changes.
let remoteStream: RemoteStream
NotificationCenter.default.addObserver(self,
selector: #selector(onRemoteStreamConnectionChange(_:)),
name: RemoteStreamNotification.connectionStateDidChange,
object: remoteStream)
@objc func onRemoteStreamConnectionChange(_ notif: NSNotification) {
// Get the new `connectionState` from the notification
let connectionStateKey = RemoteStreamNotification.Key.newConnectionState
guard let state = notif.userInfo?[connectionStateKey] as?
RemoteStream.ConnectionState else { return }
switch state {
case .connecting:
case .connected:
case .terminated:
// Optionally check the termination cause here
let terminationCause = remoteStream.terminationCause
}
}
See RemoteStreamNotification
for other notifications that you can receive.
Configure audio manually
If your app has specific, global audio needs, you can use AudioDeviceConfig
to configure how our SDK uses audio.
Deactivating the AVAudioSession when the SDK is idle
If your app has audio that you want to keep playing when joining or leaving a channel, set deactivateAVAudioSessionOnIdle
to false
.
// The Engine singleton provides access to the `AudioDeviceConfig` object.
let audioDeviceConfig = Engine.sharedInstance.audioDeviceConfig
// Enable the audio device config and disable deactivating the AVAudioSession on idle.
audioDeviceConfig.isEnabled = true
audioDeviceConfig.deactivateAVAudioSessionOnIdle = false
Full manual control of audio
We only recommend changing this setting if you fully understand how iOS audio works with AVAudioSession
.
// The Engine singleton provides access to the `AudioDeviceConfig` object.
let audioDeviceConfig = Engine.sharedInstance.audioDeviceConfig
// Disable the audio device config to give your app full control over the AVAudioSession.
audioDeviceConfig.isEnabled = false
If your app controls the AVAudioSession
, we recommend that you listen for audio-specific termination causes. These show if your current AVAudioSession
setup conflicts with how you are using the SDK.
For example, some values for AVAudioSession.Category
do not allow mic access. If you try to start a local stream, it terminates with the cause noMicrophoneAccess
.
let channel: Channel
NotificationCenter.default.addObserver(self,
selector: #selector(onChannelJoinStateChange(_:)),
name: ChannelNotification.joinStateDidChange, object: channel)
@objc func onChannelJoinStateChange(_ notif: NSNotification) {
guard let state = notif.userInfo?[ChannelNotification.Key.newJoinState] as?
Channel.JoinState else { return }
if state == .terminated && channel.terminationCause == .noAudioOutput {
// This shows that your app's AVAudioSession settings do not allow you to
// play audio. Update your settings.
}
}
let localStream: LocalStream
NotificationCenter.default.addObserver(self,
selector: #selector(onLocalStreamConnectionChange(_:)),
name: LocalStreamNotification.connectionStateDidChange, object: localStream)
@objc func onLocalStreamConnectionChange(_ notif: NSNotification) {
guard let state = notif.userInfo?[LocalStreamNotification.Key.newConnectionState] as?
LocalStream.ConnectionState else { return }
if state == .terminated && localStream.terminationCause == .noMicrophoneAccess {
// This shows that your app's AVAudioSession settings do not allow you to
// capture audio input. Update your settings.
}
}
More info
See the full API reference.
Download our sample app.