iOS SDK Overview
The iOS SDK is part of the Phenix Apple SDK. Its APIs are split into two main categories: Express and Low-Level.
We recommend that you start with the Express API as this:
- Simplifies integration
- Automatically recovers from stream failures
- Handles edge cases such as network reconnects
If you find that the Express API doesn't work for you, take a look at our Lower Level API.
Setup
The Phenix SDK is provided as
Debugging
In order to identify issues it can be helpful to see what is happening at the Phenix SDK level. Phenix logs are disabled by default in the Xcode console. To enable them, set the following environment variable (via Xcode menu: Product -> Scheme -> Edit Scheme… -> Run -> Arguments -> Environment Variables):
Name: PHENIX_LOGGING_CONSOLE_LOG_LEVEL
Value: Debug
To reduce output, you can change the value to Info or Warn for instance. You will only be able to view these logs when either running in the simulator or with a device attached to Xcode.
You can also retrieve the Phenix SDK logs programmatically using the collectLogMessages
PCast™ API.
import PhenixSdk
let pcast: PhenixPCast = ... // previously obtained
pcast.collectLogMessages({ (pcast: PhenixPCast?, status: PhenixRequestStatus, messages: String?) in
guard let messages = messages, status == .ok else {
// Handle error
return
}
let messagesArray = messages.components(separatedBy: "\n")
for message in messagesArray {
// Do something with 'message'
}
})
Notes:
messages
string contains the most recent as well as initial logs (since your app started)- Each log entry in the string is separated by a newline
- Log entries are stored chronologically, newest logs last
Observable
Observe value changes. Similar to the ReactiveX Behavior Subject.
Important differences for Phenix SDK observables:
- There is always an initial value (which can be
nil
for some objects) - Only if observed value changes will subscribers get notified
Point 1 means that when subscribing, you will always get notified immediately with the current value. This is guaranteed atomic, meaning that you do not have to worry about missing any observable updates.
PhenixObservable
Name | Signature | Returns | Description |
---|---|---|---|
subscribe | (onChanged) | PhenixDisposable | See Subscribe to an Observable |
getValue | () | varies | See Get Observable Value |
setValue | (value) | void | See Set Observable Value |
Subscribe to an Observable
Receive a notification every time the observable value changes.
@import PhenixSdk;
PhenixObservable<NSString*>* observable = ...; // previously obtained
id<PhenixDisposable> subscription =
[observable subscribe:^(PhenixObservableChange<NSString*>* change) {
NSString* newStringValue = change.value;
// Do something with newStringValue
}];
// Releasing subscription will cancel it:
subscription = nil;
Parameters
Name | Type | Description |
---|---|---|
onChanged (required) | Function | Function to call when value changes |
PhenixDisposable
Has no methods. Dropping the last reference will automatically cancel the subscription.
Get Observable Value
Synchronous method allowing retrieval of currently set value. Thread safe.
Note: Since enums are not full objects, they are proxied via NSNumber
.
@import PhenixSdk;
PhenixObservable<NSNumber*>* roleObservable = ...; // previously obtained
PhenixMemberRole role = (PhenixMemberRole)[roleObservable getValue].intValue;
PhenixObservable<NSString*>* stringObservable = ...; // previously obtained
NSString* stringValue = [stringObservable getValue];
In swift, observables containing
NSArray
need to be cast further:
import PhenixSdk
let member: PhenixMember = ... // previously obtained
let observable: PhenixObservable<NSArray> = (member.getObservableStreams())!
let streams: [PhenixStream] = observable.getValue() as! [PhenixStream]
Set Observable Value
Change the observable value. This will trigger notifications on all subscribers.
Enums require special attention when being set.
@import PhenixSdk;
PhenixObservable<NSNumber*>* roleObservable = ...; // previously obtained
[roleObservable setValue:[NSNumber numberWithInt:PhenixMemberRolePresenter]];
PhenixObservable<NSString*>* stringObservable = ...; // previously obtained
[stringObservable setValue:@"new value"];
Override Playout Delay
Example Code for overriding the playout delay via a PhenixMediaStream object
import PhenixSdk
class SomeClass {
private var currentRendererPlayoutDelayOverride: PhenixDisposable?
private var currentRenderer: PhenixRenderer?
private func setPlayoutDelayOverrideFor10Seconds() {
// Previously obtained
let mediaStream: PhenixMediaStream = ...
self.currentRenderer = mediaStream.createRenderer()
// Override playout delay to 900ms for 10 seconds
let playoutDelay: TimeInterval = 0.9
self.currentRendererPlayoutDelayOverride = self.currentRenderer?.overridePlayoutDelay(playoutDelay)
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
// Dropping the disposable will undo the playout delay override
self.currentRendererPlayoutDelayOverride = nil
}
}
}
Example Code for limiting video bandwidth with a PhenixExpressSubscriber object
import PhenixSdk
class SomeClass {
private var currentRendererPlayoutDelayOverride: PhenixDisposable?
private var currentRenderer: PhenixRenderer?
private func setPlayoutDelayOverrideFor10Seconds() {
// Previously obtained
let subscriber: PhenixExpressSubscriber = ...
self.currentRenderer = subscriber.createRenderer()
// Override playout delay to 900ms for 10 seconds
let playoutDelay: TimeInterval = 0.9
self.currentRendererPlayoutDelayOverride = self.currentRenderer?.overridePlayoutDelay(playoutDelay)
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
// Dropping the disposable will undo the playout delay override
self.currentRendererPlayoutDelayOverride = nil
}
}
}
The playout delay represents the amount of time by which audio and video are delayed when content is rendered by a subscriber; i.e. it works as a buffer. The delay adds to the overall end-to-end latency experienced by the user (on top of capture, encoding, and network latencies). It is necessary to handle network-related fluctuations (such as jitter or data loss). By default, the playout delay for real-time streams is set to 230ms. The following API allows app developers to override this default value.
In order to access the API, a reference to a PhenixRenderer
is needed. It can be obtained either by creating it from PhenixMediaStream
or PhenixExpressSubscriber
, or via several of the Express APIs, which can return renderers (joinChannel, subscribeToMemberStream, subscribe).
The returned disposable allows control over how long the override should stay in effect; it therefore needs to be held onto via a strong reference. If overridePlayoutDelay
is called multiple times before any of the previous disposables are released, then only the most recent override will remain in effect until its disposable is released. Releasing any of the disposables from earlier overridePlayoutDelay
calls will have no effect.
Notes:
- The override represents an absolute value, not a delta.
- If an override increases the playout delay, it will result in content being paused. Example: changing a delay of 1 second to 5 seconds will cause the content to be paused for roughly 4 seconds.
- If an override decreases the playout delay, it will cause a jump where some of the content will be skipped.
- Very large override values will increase the amount of memory consumed. It is generally recommended to stay below 10 seconds.
Parameters
Name | Type | Description |
---|---|---|
desiredPlayoutDelay (required) | TimeInterval | Desired playout delay |
Returns
Type | Description |
---|---|
PhenixDisposable | Ensures override is kept in effect for as long as a strong reference is held |