iOS SDK Overview
The iOS SDK 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 standard iOS XCFramework. Please see our public Github repository for details.
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.
1import PhenixSdk23let pcast: PhenixPCast = ... // previously obtained45pcast.collectLogMessages({ (pcast: PhenixPCast?, status: PhenixRequestStatus, messages: String?) in6 guard let messages = messages, status == .ok else {7 // Handle error8 return9 }1011 let messagesArray = messages.components(separatedBy: "\n")12 for message in messagesArray {13 // Do something with 'message'14 }15})
1@import PhenixSdk;23id<PhenixPCast> pcast = ...; // previously obtained45[pcast collectLogMessages:^(id<PhenixPCast> pcast, PhenixRequestStatus status, NSString* messages) {6 if (status != PhenixRequestStatusOk) {7 // Handle error8 return;9 }1011 NSArray* messagesArray = [messages componentsSeparatedByString:@"\n"];12 for (NSString* message in messagesArray) {13 // Do something with 'message'14 }15 }];
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.
1@import PhenixSdk;23PhenixObservable<NSString*>* observable = ...; // previously obtained45id<PhenixDisposable> subscription =6 [observable subscribe:^(PhenixObservableChange<NSString*>* change) {7 NSString* newStringValue = change.value;8 // Do something with newStringValue9 }];1011// Releasing subscription will cancel it:12subscription = nil;
1import PhenixSdk23let observable: PhenixObservable<String> = ... // previously obtained45var subscription = observable.subscribe({ change in6 if let change = change, let newStringValue = change.value {7 // Do something with newStringValue8 })910// Releasing subscription will cancel it:11subscription = 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
.
1@import PhenixSdk;23PhenixObservable<NSNumber*>* roleObservable = ...; // previously obtained4PhenixMemberRole role = (PhenixMemberRole)[roleObservable getValue].intValue;56PhenixObservable<NSString*>* stringObservable = ...; // previously obtained7NSString* stringValue = [stringObservable getValue];
1import PhenixSdk23let roleObservable: PhenixObservable<NSNumber> = ... // previously obtained4let role = PhenixMemberRole(rawValue: roleObservable.getValue().intValue)56let stringObservable: PhenixObservable<NSString> = ... // previously obtained7let stringValue: String = stringObservable().getValue() as String
In swift, observables containing
NSArray
need to be cast further:
1import PhenixSdk23let member: PhenixMember = ... // previously obtained45let observable: PhenixObservable<NSArray> = (member.getObservableStreams())!6let 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.
1@import PhenixSdk;23PhenixObservable<NSNumber*>* roleObservable = ...; // previously obtained4[roleObservable setValue:[NSNumber numberWithInt:PhenixMemberRolePresenter]];56PhenixObservable<NSString*>* stringObservable = ...; // previously obtained7[stringObservable setValue:@"new value"];
1import PhenixSdk23let roleObservable: PhenixObservable<NSNumber> = ... // previously obtained4roleObservable.setValue(NSNumber(value: PhenixMemberRole.presenter.rawValue))56let stringObservable: PhenixObservable<NSString> = ... // previously obtained7stringObservable.setValue("new value")
Override Playout Delay
Example Code for overriding the playout delay via a PhenixMediaStream object
1import PhenixSdk23class SomeClass {4 private var currentRendererPlayoutDelayOverride: PhenixDisposable?5 private var currentRenderer: PhenixRenderer?67 private func setPlayoutDelayOverrideFor10Seconds() {8 // Previously obtained9 let mediaStream: PhenixMediaStream = ...1011 self.currentRenderer = mediaStream.createRenderer()1213 // Override playout delay to 900ms for 10 seconds14 let playoutDelay: TimeInterval = 0.91516 self.currentRendererPlayoutDelayOverride = self.currentRenderer?.overridePlayoutDelay(playoutDelay)1718 DispatchQueue.main.asyncAfter(deadline: .now() + 10) {19 // Dropping the disposable will undo the playout delay override20 self.currentRendererPlayoutDelayOverride = nil21 }22 }23}
Example Code for limiting video bandwidth with a PhenixExpressSubscriber object
1import PhenixSdk23class SomeClass {4 private var currentRendererPlayoutDelayOverride: PhenixDisposable?5 private var currentRenderer: PhenixRenderer?67 private func setPlayoutDelayOverrideFor10Seconds() {8 // Previously obtained9 let subscriber: PhenixExpressSubscriber = ...1011 self.currentRenderer = subscriber.createRenderer()1213 // Override playout delay to 900ms for 10 seconds14 let playoutDelay: TimeInterval = 0.91516 self.currentRendererPlayoutDelayOverride = self.currentRenderer?.overridePlayoutDelay(playoutDelay)1718 DispatchQueue.main.asyncAfter(deadline: .now() + 10) {19 // Dropping the disposable will undo the playout delay override20 self.currentRendererPlayoutDelayOverride = nil21 }22 }23}
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 |