NAV Navbar
Phenix logo
Version 2022.0.9
ObjectiveC Swift

iOS SDK - Low Level APIs

The following low level APIs are provided in the iOS sdk:

PCast SDK

Initializing

Before using a PCast object, you need to initialize it.

@import PhenixSdk;

id<PhenixPCast> pcast = [PhenixPCastFactory createPCast];

// #1 Default initialization options
[pcast initialize];

// OR

// #2 Custom initialization options
PhenixPCastInitializeOptions* initOptions = [PhenixPCastInitializeOptions new];
initOptions.enableProcessTerminationSignalHandling = NO;

[pcast initialize:initOptions];

Initialize Options

Name Description
enableProcessTerminationSignalHandling Controls if process termination signal handling is enabled or not
configureLogging Control if phenix logging is enabled
streamingSourceMapping Allows partial override of streaming source URIs

Stream Source Mapping

Optional field that can be provided with PhenixPCastInitializeOptions. It only has an effect on the subscriber side, and only for streams with the “streaming” capability (on both publisher and subscriber side). The property allows you to redirect requests from the SDKs player to your own CDN.

@import PhenixSdk;

id<PhenixPCastInitializeOptions> initOptions = [PhenixPCastInitializeOptions new];
initOptions.streamingSourceMapping = [PhenixStreamingSourceMapping new];

initOptions.streamingSourceMapping.patternToReplace = @"https:\\/\\/phenixrts\\.com\\/video";
initOptions.streamingSourceMapping.replacement = @"https://myown.cdn.com";
Name Description
patternToReplace A regular expression indicating with part of the incoming streaming source URI to replace
replacement The replacement string to insert into the streaming source URI

Note: The regular expression has to be properly escaped to work correctly.

Connect And Authenticate

@import PhenixSdk;

// Obtained via Admin API
NSString* authenticationToken = ...;
// Previously initialized
id<PhenixPCast> pcast = ...;

[pcast start:
    authenticationToken:
    ^(id<PhenixPCast> pcast, PhenixRequestStatus status, NSString* sessionId) {
        if (status == PhenixRequestStatusOk) {
            NSLog(@"PCast started...");
        } else {
            NSLog(@"Failed to start PCast...");
        }
    }:
    ^(id<PhenixPCast> pcast) {
        NSLog(@"We are online...");
    }:
    ^(id<PhenixPCast> pcast) {
        NSLog(@"We are offline...");
    }];

Parameters

Name Type Description
authenticationToken (required) string The authentication token generated using the Admin API
authenticationCallback (required) lambda (pcast, status, sessionId) Called upon successful authentication or when authentication failed or has to be redone. Upon successful authentication, the authenticationCallback will be called with status=PhenixRequestStatusOk. If at any time a new authenticationToken is required, then the authenticationCallback is called with status=PhenixRequestStatusUnauthorized to indicate that we are no longer authenticated.
onlineCallback (required) lambda (pcast) Called when the client is connected to the streaming platform
offlineCallback (required) lambda (pcast) Called when the client is disconnected from the streaming platform. Ongoing streams may continue while we are temporarily disconnected. However, no new streams can be started while being disconnected. The client automatically tries to reconnect and will call onlineCallback when it succeeds in doing so or eventually call authenticationCallback to indicate that re-authentication is required.

Authentication Callback Status Codes

Status Valid Fields Description
PhenixRequestStatusOk sessionId Authentication succeeded, the sessionId is populated
PhenixRequestStatusUnauthorized <none> Authentication failed or re-authentication required
<varies> <none> Authentication failed for other reasons

Disconnecting

@import PhenixSdk;

// Previously initialized and started
id<PhenixPCast> pcast = ...;

[pcast stop];

// Once you are done using PCast (e.g. exiting the app)
[pcast shutdown];

Get Local User Media

@import PhenixSdk;

// Previously initialized and started
id<PhenixPCast> pcast = ...;

PhenixUserMediaOptions* gumOptions = [PhenixUserMediaOptions new]

// Customize options if desired
gumOptions.video.capabilityConstraints
    [[NSNumber numberWithInteger:PhenixDeviceCapabilityFacingMode]] =
    @[ [PhenixDeviceConstraint initWithFacingMode:PhenixFacingModeUser] ];
gumOptions.video.capabilityConstraints
    [[NSNumber numberWithInteger: PhenixDeviceCapabilityFlashMode]] =
    @[ [PhenixDeviceConstraint initWithFlashMode:PhenixFlashModeAlwaysOff] ];
gumOptions.video.capabilityConstraints
    [[NSNumber numberWithInteger: PhenixDeviceCapabilityHeight]] =
    @[ [PhenixDeviceConstraint initWithDouble:720:PhenixConstraintTypeExact] ];
gumOptions.video.capabilityConstraints
    [[NSNumber numberWithInteger: PhenixDeviceCapabilityWidth]] =
    @[ [PhenixDeviceConstraint initWithDouble:800:PhenixConstraintTypeMin],
       [PhenixDeviceConstraint initWithDouble:1500:PhenixConstraintTypeMax] ];
gumOptions.audio.enabled = NO;

[pcast getUserMedia:
    gumOptions:
    ^(id<PhenixPCast> pcast,
      PhenixRequestStatus status,
      id<PhenixUserMediaStream> userMediaStream) {
        // Check status and store 'userMediaStream'
    }];

Parameters

Name Type Description
options (required) PhenixUserMediaOptions The options defining the requested user media stream
userMediaCallback (required) lambda (pcast, status, userMediaStream) Upon acquiring of the user media stream, the userMediaCallback will be called with status=PhenixRequestStatusOk. If the user media is currently used by another application, then you may receive a code status=PhenixRequestStatusConflict . If the operation fails with status=PhenixRequestStatusFailed then please check the logs for more information

Device Capability

Note: iPhone models earlier than the iPhone 6 will be limited to publishing at 720p due to hardware limitations.

Name Description
PhenixDeviceCapabilityWidth Width in pixels
PhenixDeviceCapabilityHeight Height in pixels
PhenixDeviceCapabilityFrameRate Number of frames per second
PhenixDeviceCapabilityFacingMode Facing mode
PhenixDeviceCapabilityFlashMode Flash mode
PhenixDeviceCapabilityDeviceId Device ID string (obtain from here)
PhenixDeviceCapabilityLocation Device Location
PhenixDeviceCapabilityPolarPattern Polar pattern
PhenixDeviceCapabilityAudioEchoCancelationMode Audio echo cancelation mode
PhenixVideoSourceRotationMode Video source rotation mode

Constraint Type

Name Description
PhenixConstraintTypeMin Hard constraint: Capability must have at least the specified value
PhenixConstraintTypeMax Hard constraint: Capability must have at most the specified value
PhenixConstraintTypeExact Hard constraint: Capability must have exactly the specified value
PhenixConstraintTypeIdeal Soft constraint: Capability should have specified value, but other values are acceptable (default)

Facing Mode

Name Description
PhenixFacingModeAutomatic Select a facing mode automatically (default)
PhenixFacingModeEnvironment Facing the surrounding environment (e.g., back camera)
PhenixFacingModeUser Facing the user (e.g., front camera)

Flash Mode

Only applicable to video devices

Name Description
PhenixFlashModeAutomatic Flash is turned on automatically when needed (default)
PhenixFlashModeAlwaysOn Flash is on (if available)
PhenixFlashModeAlwaysOff Flash is off

Device Location

Name Description
PhenixLocationAutomatic Select any device (default)
PhenixLocationUpper Mounted on top of phone/tablet
PhenixLocationLower Mounted at bottom of phone/tablet

Polar Pattern

Only applicable to audio devices

Name Description
PhenixPolarPatternAutomatic Automatically select pattern (default)
PhenixPolarPatternOmnidirectional Equally sensitive to sound from any direction
PhenixPolarPatternCardioid Most sensitive to sound from the direction in which the data source points and is (nearly) insensitive to sound from the opposite direction
PhenixPolarPatternSubcardioid Most sensitive to sound from the direction in which the data source points and is less sensitive to sound from the opposite direction

Audio Echo Cancelation Mode

Only applicable to audio devices

Name Description
PhenixAudioEchoCancelationModeAutomatic Automatically select AEC (default)
PhenixAudioEchoCancelationModeOn Enable AEC if available
PhenixAudioEchoCancelationModeOff Disabled AEC

Video source rotation mode

Only applicable to video devices. Determines how to orient captured video frames.

Follow device rotation will ensure that video frames match the orientation in which the user is holding the device, regardless of how the app UI may be oriented. This is generally the behavior expected by a user, i.e. if the user is holding the device sideways, then video should be in landscape mode.

Following UI rotation allows your app to keep the video orientation locked to the UI rotation, regardless of how the user is holding the device. This makes it possible for instance to lock your UI in portrait mode, and have portrait video output even if the user is holding the device sideways.

Name Description
PhenixVideoSourceRotationModeAutomatic Automatically select rotation mode (default)
PhenixVideoSourceRotationModeFollowDeviceRotation Video frames oriented according to how device is held
PhenixVideoSourceRotationModeFollowUiRotation Video frames oriented according to UI orientation

Updating Options

Sometimes you find it useful to change the camera while a stream is running or just would like to turn on the flash light temporarily.

@import PhenixSdk;

// Previously obtained via 'getUserMedia'
id<PhenixUserMediaStream> userMediaStream = ...;

// Previously initialized and used with 'getUserMedia'
PhenixUserMediaOptions* gumOptions;

[gumOptions.video.capabilityConstraints
    [[NSNumber numberWithInteger:PhenixDeviceCapabilityFacingMode]][0]
        updateFacingMode:PhenixFacingModeEnvironment];
gumOptions.video.capabilityConstraints
    [[NSNumber numberWithInteger:PhenixDeviceCapabilityFlashMode]][0]
        updateFlashMode: PhenixFlashModeAlwaysOn];

[userMediaStream applyOptions:gumOptions];

Enumerating Source Devices

You can get a list of available source devices.

@import PhenixSdk;

// Previously initialized and started
id<PhenixPCast> pcast = ...;

[pcast enumerateSourceDevices:^(id<PhenixPCast> pcast, NSArray<PhenixSourceDeviceInfo*>* devices) {
    // Store devices as needed
}:PhenixMediaTypeVideo];

Parameters

Name Type Description
mediaType (required) PhenixMediaType The media type for which to enumerate source devices

PhenixSourceDeviceInfo fields

Name Type Description
id NSString Source device ID
name NSString Source device Name
mediaType PhenixMediaType Source device media type
deviceType PhenixSourceDeviceType Source device type
facingMode PhenixFacingMode Source device facing mode

Media Type

Name Description
PhenixMediaTypeVideo Video
PhenixMediaTypeAudio Audio

Source Device Type

Name Description
PhenixSourceDeviceTypeNull Null device (e.g. blank screen or silence)
PhenixSourceDeviceTypePhysical Physical device (e.g. camera or microphone)
PhenixSourceDeviceTypeSystemOutput System output capture (screencast)
PhenixSourceDeviceTypeSynthetic Synthetic source, used for testing
PhenixSourceDeviceTypeUri Uri source, used to stream from uri

Publish a Stream

@import PhenixSdk;

// Previously initialized and started
id<PhenixPCast> pcast = ...;
// Previously obtained via Admin API
NSString* streamToken = ...;
// Previously obtained via either PCast.subscribe or PhenixUserMediaStream.mediaStream
id<PhenixMediaStream> mediaStream = ...;

NSArray* tags = @[ @"my-tag" ];

[pcast publish:
    streamToken:
    mediaStream:
    ^(id<PhenixPCast> pcast, PhenixRequestStatus status, id<PhenixPublisher> publisher) {
        // Check status and store 'publisher'

        // The "streamId" of the publisher
        NSString* streamId = publisher.streamId;

        if (publisher.hasEnded == YES) {
            // Checks if the publisher has ended
        }

        // Attach publisher ended callback
        [publisher setPublisherEndedCallback:
            ^(id<PhenixPublisher> publisher,
              PhenixStreamEndedReason reason,
              NSString* reasonDescription) {
                // Called when the stream has ended
                NSLog(@"Publish stream ended with reason [%@]", reasonDescription);
            }];

        // To stop later
        [publisher stop:@"I-am-done-publishing"];
    }:
    tags];

Parameters

Name Type Description
streamToken (required) string The publish token is generated using the Admin API
mediaStream (required) PhenixMediaStream The user media stream acquired through PCast.subscribe(…) or locally with PhenixUserMediaStream.mediaStream
publishCallback (required) lambda Called upon completion of the operation
tags (optional) array of strings Tags that will be provided with the stream notifications to your backend callback endpoint

PhenixStreamEndedReason

Reason Description
PhenixStreamEndedReasonEnded The stream ended normally
PhenixStreamEndedReasonFailed The stream failed
PhenixStreamEndedReasonCensored The stream was censored
PhenixStreamEndedReasonMaintenance A maintenance event caused this stream to be terminated
PhenixStreamEndedReasonCapacity The stream was terminated due to capacity limitations
PhenixStreamEndedReasonAppBackground The stream was terminated due to the mobile app entering into the background
PhenixStreamEndedReasonCustom A custom termination reason is provided in the “reasonDescription” field

Subscribe to a Stream

@import PhenixSdk;

// Previously initialized and started
id<PhenixPCast> pcast = ...;
// Previously obtained via Admin API
NSString* streamToken = ...;

[pcast subscribe:
    streamToken:
    ^(id<PhenixPCast> pcast,
      PhenixRequestStatus status,
      id<PhenixMediaStream> mediaStream) {
        // Check status and store 'mediaStream'

        // Attach stream ended callback
        [mediaStream setStreamEndedCallback:
            ^(id<PhenixMediaStream> mediaStream,
              PhenixStreamEndedReason reason,
              NSString* reasonDescription) {
                NSLog(@"Subscriber stream ended with reason [%@]", reasonDescription);
        }];

        // To stop later
        [mediaStream stop];
    }];

Parameters

Name Type Description
streamToken (required) string The publish token is generated using the Admin API
subscribeCallback (required) lambda Called upon completion of the operation

View a Stream

In order to view a stream you have to attach it to a render surface.

@import PhenixSdk;

// Previously obtained via either PCast.subscribe or PhenixUserMediaStream.mediaStream
id<PhenixMediaStream> mediaStream = ...;

id<PhenixRenderer> renderer = [mediaStream createRenderer];

[renderer setRenderSurfaceReadyCallback:
    ^(id<PhenixRenderer> renderer, CALayer* renderSurface) {
        // Attach layer to UI and set fill options and/or resize as needed
        // NOTE: This may be called more than once (e.g. after interruptions)
    }];

PhenixRendererStartStatus status = [renderer start];

// To stop later
[renderer stop];

Renderer options

It is possible to pass additional options when creating a renderer.

@import PhenixSdk;

// Previously obtained via either PCast.subscribe or PhenixUserMediaStream.mediaStream
id<PhenixMediaStream> mediaStream = ...;

PhenixRendererOptions* options = [PhenixRendererOptions new];
options.aspectRatioMode = PhenixAspectRatioModeFill;
options.useNullVideoDevice = NO;
options.useNullAudioDevice = YES;


id<PhenixRenderer> renderer = [mediaStream createRenderer:options];

Properties

Name Type Default Description
aspectRatioMode (optional) PhenixAspectRatioMode PhenixAspectRatioModeFill How to fill available video render surface
useNullAudioDevice (optional) BOOL false Audio will not be routed to a physical renderer device if true
useNullVideoDevice (optional) BOOL false Video will not be routed to a physical renderer device if true.
hardwareAcceleratedDecodingMode (optional) PhenixHardwareAcceleratedDecodingMode PhenixHardwareAcceleratedDecodingModeAutomatic Hardware accelerated decoding mode

When using null devices you can still use the frame-ready API to receive the raw audio and/or video frames.

Aspect Ratio Mode

Name Description
PhenixAspectRatioModeAutomatic Defaults to fill
PhenixAspectRatioModeFill Fill entire render area. Video may be truncated
PhenixAspectRatioModeLetterbox Black bars are added on sides or top/bottom of render area, video will not be truncated

Hardware Accelerated Decoding Mode

Name Description
PhenixHardwareAcceleratedDecodingModeAutomatic Use hardware decoding on certified devices
PhenixHardwareAcceleratedDecodingModeOn Always use hardware decoding
PhenixHardwareAcceleratedDecodingModeOff Always use software decoding

Preview Local User Media

@import PhenixSdk;

// Previously obtained via 'getUserMedia'
id<PhenixUserMediaStream> userMediaStream = ...;

id<PhenixRenderer> renderer = [userMediaStream.mediaStream createRenderer];

Muting and Unmuting of Audio

@import PhenixSdk;

// Previously obtained from media stream
id<PhenixRenderer> renderer = ...;

BOOL isMuted = renderer.audioMuted;
[renderer muteAudio];
[renderer unmuteAudio];

Taking a Screenshot

If you like to show a preview, you can take a still image from a renderer.

@import PhenixSdk;

// Previously obtained from media stream and started
id<PhenixRenderer> renderer = ...;

[renderer setLastVideoFrameRenderedReceivedCallback:
    ^(id<PhenixRenderer> pcast, CVPixelBufferRef nativeVideoFrame) {
        // NOTE: If frame is dispatched to another thread, ensure its reference
        //       is kept alive by using CVPixelBufferRetain/CVPixelBufferRelease
    }];
[renderer requestLastVideoFrameRendered];

Data Quality Feedback

If you like to show the user feedback about how their internet connectivity affects the stream quality, you can listen for data quality notifications.

@import PhenixSdk;

// Previously obtained from media stream and started
id<PhenixRenderer> renderer = ...;

[renderer setDataQualityChangedCallback:
    ^(id<PhenixRenderer> renderer,
      PhenixDataQualityStatus status,
      PhenixDataQualityReason reason) {
        // Inform user, take action based on status and reason
    }];

// Previously obtained via PCast.publish
id<PhenixPublisher> publisher = ...;

[publisher setDataQualityChangedCallback:
    ^(id<PhenixPublisher> publisher,
      PhenixDataQualityStatus status,
      PhenixDataQualityReason reason) {
        // Inform user, take action based on status and reason
    }];

Data Quality Status For Publishers

Status Reason Description
PhenixDataQualityStatusNoData PhenixDataQualityReasonNone The publisher has a bad internet connection and no data is being streamed.
PhenixDataQualityStatusNoData PhenixDataQualityReasonUploadLimited The publisher has a bad internet connection and no data is being streamed.
PhenixDataQualityStatusAll PhenixDataQualityReasonNone Good internet connection and no quality reduction in effect.
PhenixDataQualityStatusAll PhenixDataQualityReasonUploadLimited The publisher has a slow internet connection and the quality of the stream is reduced.
PhenixDataQualityStatusAll PhenixDataQualityReasonNetworkLimited Subscribers have bad internet connections and the quality of the stream is reduced.
PhenixDataQualityStatusAudioOnly PhenixDataQualityReasonUploadLimited The publisher has a bad internet connection and only audio is streamed.
PhenixDataQualityStatusAudioOnly PhenixDataQualityReasonNetworkLimited Subscribers have bad internet connections and only audio is streamed.

Data Quality Status For Viewers

Status Reason Description
PhenixDataQualityStatusNoData PhenixDataQualityReasonNone The subscriber has a bad internet connection and no data is being received.
PhenixDataQualityStatusNoData PhenixDataQualityReasonDownloadLimited The subscriber has a bad internet connection and no data is being received.
PhenixDataQualityStatusNoData PhenixDataQualityReasonPublisherLimited The publisher has a bad internet connection and no data is being received.
PhenixDataQualityStatusNoData PhenixDataQualityReasonNetworkLimited The network is limiting the quality of the stream and no data is being received.
PhenixDataQualityStatusAll PhenixDataQualityReasonNone Good internet connection and no quality reduction in effect.
PhenixDataQualityStatusAll PhenixDataQualityReasonDownloadLimited The subscriber has a bad internet connection and the quality of the stream is reduced.
PhenixDataQualityStatusAll PhenixDataQualityReasonPublisherLimited The publisher has a bad internet connection and the quality of the stream is reduced.
PhenixDataQualityStatusAll PhenixDataQualityReasonNetworkLimited Other subscribers have bad internet connections and the quality of the stream is reduced.
PhenixDataQualityStatusAudioOnly PhenixDataQualityReasonNone Audio only stream, good internet connection and no quality reduction in effect.
PhenixDataQualityStatusAudioOnly PhenixDataQualityReasonDownloadLimited The subscriber has a bad internet connection and is only receiving audio.
PhenixDataQualityStatusAudioOnly PhenixDataQualityReasonPublisherLimited The publisher has a bad internet connection and the subscriber is only receiving audio.
PhenixDataQualityStatusAudioOnly PhenixDataQualityReasonNetworkLimited The network is limiting the quality of the stream and the subscriber is only receiving audio.

Handling Dimension Changes

Cameras may be switched at runtime and devices may be rotated. Register a handler to receive a notification whenever the video dimension changes.

@import PhenixSdk;

// Previously obtained from media stream
id<PhenixRenderer> renderer = ...;

[renderer setVideoDisplayDimensionsChangedCallback:
    ^(id<PhenixRenderer> renderer, const struct PhenixDimensions* displayDimensions) {
        // displayDimensions->width, displayDimensions->height
        // Can get called multiple times while rendering a stream
    }];

Room Service

Set up a room and manage members, streams, and chat. Some typical uses for rooms include:

To get started, create a room of the type associated with your need.

Initializing

First you will need to initialize a room service object. This object is used for creating, joining, and leaving a room. As well, use this object for managing the room you enter (active room), members, and the model of your Self.

Note: PhenixPCast must be initialized and started when invoking createRoomService so that a valud session ID is present.

import PhenixSdk

let pcast: PhenixPCast = ...  // previously obtained
let roomService = PhenixRoomServiceFactory.createRoomService(pcast)!

Parameters

Name Type Description
pcast (required) PhenixPCast Instantiated PCast object. Must already be authenticated

PhenixRoomService

Name Signature Returns Description
getRoomInfo (roomId, alias, callback) PhenixRoom See Get Room Info
createRoom (room, callback) PhenixRoom See Create a Room
joinRoom (roomId, alias, callback) PhenixRoom See Join a Room
leaveRoom (callback) void See Leave a Room
destroyRoom (callback) void See Destroy a Room
getSelf () Observable of PhenixMember) Observable of self member
getObservableActiveRoom () Observable of PhenixRoom) Observable of the active room

Instantiate Self Member Model

Before entering a room the PhenixRoomService must have a valid model of the Self member. The Self member model may be updated at any time by changing observable values and calling commitChanges. See Update Self Model.

Update Self Model

Broadcast local changes of the Self model to all the members in the room. These changes are sent to our server which forwards the changes to all members in the room. This works whether or not you are currently in a room.

import PhenixSdk

let roomService: PhenixRoomService = ...  // previously obtained
let selfMember = roomService.getSelf()

selfMember.getObservableScreenName().setValue("My New Screen Name")
selfMember.getObservableRole().setValue(NSNumber.init(value: PhenixMemberRole.audience.rawValue))

// Commit changes to self if already in a room
selfMember.commitChanges({ (status: PhenixRequestStatus, message: String?) in
    if status == .ok {
      // Successfully updated self
    } else {
      // Handle error
    }
  })

To undo uncommitted local changes, use reload:

import PhenixSdk

let roomService: PhenixRoomService = ...  // previously obtained
let selfMember = roomService.getSelf()

selfMember.getObservableScreenName().setValue("My New Screen Name")
selfMember.getObservableRole().setValue(NSNumber.init(value: PhenixMemberRole.audience.rawValue))

// Decided to undo changes
selfMember.reload()

Update Self Callback Arguments

Name Type Description
status PhenixRequestStatus The status of the operation
message String Error message, if applicable

Set Self Streams

The self model must have at least one stream when joining the room as any member role besides Audience.

import PhenixSdk

let roomService: PhenixRoomService = ...  // previously obtained
let selfMember = roomService.getSelf()

let selfStream = roomService.createStream("http://myuri.stream", .user, .enabled, .enabled)

selfMember.getObservableStreams().setValue([selfStream!])

// Now commit changes

Parameters

Name Type Description
uri (required) String Unique identifier for stream.
type (required) PhenixStreamType Type of stream - see Stream Types
audioState (required) PhenixTrackState State of the audio track - see Stream Track States
videoState (required) PhenixTrackState State of the video track - see Stream Track States

Get Room Info

Get room info with the provided room details without joining the room.

import PhenixSdk

let roomService: PhenixRoomService = ...  // previously obtained

// Either provide alias or roomId, other can be nil or empty
let roomId = "us-central#demo#exampleRoomId"
let alias: String? = nil

roomService.getRoomInfo(
    roomId,
    alias,
    { (roomService: PhenixRoomService?, status: PhenixRequestStatus, room: PhenixRoom?) in
      if status == .ok {
        // Do something with room
      } else {
        // Handle error
      }
    })

Parameters

Name Type Description
roomId (required) String ID of the room
alias (optional) String Alternative to roomId - alias of the room
getRoomInfoCallback (required) Function Callback with the status of the request and the room model

Get Room Info Callback Arguments

Name Type Description
roomService PhenixRoomService The room service making the callback
status PhenixRequestStatus The status of the operation
room PhenixRoom Immutable room object

Get Room Info Callback Status Codes

Status Description
ok Get room info succeeded
not-found Get room info failed. Room does not exist - verify room data or create the room
<varies> Get room info failed for other reasons

Create a Room

Create a room with the provided details

import PhenixSdk

let roomService: PhenixRoomService = ...  // previously obtained

let roomName = "My Room Name"
let description = "Deep thoughts only"

let roomOptions = PhenixRoomServiceFactory.createRoomOptionsBuilder()
    .withName(roomName)
    .withDescription(description)
    .withType(.multiPartyChat)
    .buildRoomOptions()

roomService.createRoom(
    roomOptions,
    { (roomService: PhenixRoomService?, status: PhenixRequestStatus, room: PhenixRoom?) in
      if status == .ok {
        // Do something with room
      } else {
        // Handle error
      }
    })

Parameters

Name Type Description
options (required) PhenixRoomOptions Room creation options
callback (required) Function Callback with the status of the request and the room model

Create Room Callback Arguments

Name Type Description
roomService PhenixRoomService The room service making the callback
status PhenixRequestStatus The status of the operation
room PhenixRoom Immutable room object

Create Room Callback Status Codes

Status Description
PhenixRequestStatusOk Create room succeeded. This can also mean that the room already existed
<varies> Create room failed for other reasons

PhenixRoomOptionsBuilder

Name Type Default Description
withName (required) String Name of room
withType (required) PhenixRoomType Room type
withAlias (optional) String <generated> Room Alias. If not passed in, it will be generated for you
withDescription (optional) String <empty> Room description
buildRoomOptions <none> Builds the PhenixRoomOptions

Join a Room

Join a room with the Self model. After successfully joining a room that room becomes the active room and can be returned via the getObservableActiveRoom method.

import PhenixSdk

let roomService: PhenixRoomService = ...  // previously obtained

// Need to provide either room ID or alias (other can be nil)
let roomId: String? = nil
let alias = "exampleRoom"

roomService.joinRoom(
    roomId,
    alias,
    { (roomService: PhenixRoomService?, status: PhenixRequestStatus, room: PhenixRoom?) in
      if status == .ok {
        // Do something with room
      } else {
        // Handle error
      }
    })

Parameters

Name Type Description
roomId (required) String ID of the room
alias (optional) String Alternative to roomId - alias of the room
callback (required) Function Callback with the status of the request and the room model

Join Room Callback Arguments

Name Type Description
roomService PhenixRoomService The room service making the callback
status PhenixRequestStatus The status of the operation
room PhenixRoom Immutable room object

Join Room Callback Status Codes

Status Description
ok Join room succeeded
not-found Join room failed. Room does not exist - create room first
<varies> Join room failed for other reasons

Subscribe to Room Member Streams

After entering a room you may use the active room to get the observable members and associated metadata. Here we provide an example for accessing each member’s streams whenever the list of members is updated. Remember: Since each observable behaves like a Behavior Rx Subject, your subscriber will always get called back immediately with the current value (in this case the list of members), see Observable.

import PhenixSdk

let roomService: PhenixRoomService = ...  // previously obtained

let observableActiveRoom = roomService.getObservableActiveRoom()!

// For this simple example, we assume that there is an active room, hence we can force un-wrap.
// In a production environment, this can return nil if we have not currently joined a room. In that case
// you would want to listen as a subscriber on the 'observableActiveRoom' for updates
let activeRoom = observableActiveRoom.getValue()!
let observableMembers = activeRoom.getObservableMembers()!

let subscription = observableMembers.subscribe({ (change) in
  let currentMembers = change?.value as! [PhenixMember]
  for member in currentMembers {
    let currentStreams = member.getObservableStreams().getValue() as! [PhenixStream]
    // Now do something with this member's streams
  }
})

// The subscription will remain active for as long as 'subscription' is kept alive

Update Room Model

Broadcast local changes of the Room model to all the members in the room. These changes are sent to our server which forwards the changes to all members in the room.

import PhenixSdk

let roomService: PhenixRoomService = ...  // previously obtained

let room = roomService.getObservableActiveRoom().getValue()!

room.getObservableDescription().setValue("My New Room Description")
room.commitChanges({ (status: PhenixRequestStatus, message: String?) in
    if status == .ok {
      // Successfully updated room
    } else {
      // Handle error
    }
  })

Parameters

Name Type Description
callback (required) Function Callback with the status of the request

Update Room Callback Arguments

Name Type Description
status PhenixRequestStatus The status of the operation
message String Error message, if applicable

Update Room Callback Status Codes

Status Description
ok Update room succeeded
<varies> Update room failed for other reasons

Leave a Room

Leave the current active room. Self is not modified.

import PhenixSdk

let roomService: PhenixRoomService = ...  // previously obtained

roomService.leaveRoom({ (roomService: PhenixRoomService?, status: PhenixRequestStatus) in
    if status == .ok {
      // Successfully left room, no action required
    } else {
      // Handle error
    }
  })

Parameters

Name Type Description
callback (required) Function Callback with the status of the request

Leave Room Callback Arguments

Name Type Description
roomService PhenixRoomService The room service making the callback
status PhenixRequestStatus The status of the operation

Update Room Callback Status Codes

Status Description
ok Leave room succeeded
<varies> Leave room failed for other reasons

Destroy a Room

Destroys the current active room assuming your self member has the permissions.

import PhenixSdk

let roomService: PhenixRoomService = ...  // previously obtained

roomService.destroyRoom({ (roomService: PhenixRoomService?, status: PhenixRequestStatus) in
    if status == .ok {
      // Successfully destroyed room, no action required
    } else {
      // Handle error
    }
  })

Parameters

Name Type Description
callback (required) Function Callback with the status of the request

Leave Room Callback Arguments

Name Type Description
roomService PhenixRoomService The room service making the callback
status PhenixRequestStatus The status of the operation

Update Room Callback Status Codes

Status Description
ok Destroy room succeeded
<varies> Destroy room failed for other reasons

Observe Room size

Subscribe to observable to receive periodic room size updates. The room size corresponds to the total number of members currently in the room, which does include audience members.

This is a so called cold observable, which means that you may not immediately receive an update. It also means that both getValue and setValue methods will cause a failure.

Updates will be provided for as long as the disposable reference is kept alive.

import PhenixSdk

let room: PhenixImmutableRoom = ...  // previously obtained

let disposable = room.getObservableEstimatedSize().subscribe(
    { (change: PhenixObservableChange<NSNumber>?) in
      let currentEstimatedRoomSize =  change?.value.uintValue
      // E.g. update UI
    })

Room Model

The following details technical specifications for the Room, Members, and Streams.

PhenixImmutableRoom

Name Signature Returns Description
getRoomId () NSString The room ID
getObservableAlias () Observable(of NSString) Observable of room alias
getObservableName () Observable(of NSString) Observable of room name
getObservableDescription () Observable(of NSString) Observable of room description
getObservableType () Observable(of PhenixRoomType via NSNumber) Observable of room type
getObservableMembers () Observable(of NSArray of PhenixMember) Observable of array of visible members in the room
getObservableBridgeId () Observable(of NSString) Observable of room bridgeId
getObservablePin () Observable(of NSString) Observable of room pin
getObservableEstimatedSize () Observable(of size_t) Observable of estimated room size. This is a cumulative count of all members in the room, including audience members

PhenixRoom

PhenixRoom extends PhenixImmutableRoom

Name Signature Returns Description
commitChanges (RoomCommitCallback) void Attempts to update server room model with any changes made locally
reload () void Reverts all local changes to the server room model

Room Types

Type Description
PhenixRoomTypeDirectChat One to one chat
PhenixRoomTypeMultiPartyChat Many to many or few to many chat
PhenixRoomTypeModeratedChat Moderated chat with integrated SIP Bridge for conferencing abilities (join by phone).
PhenixRoomTypeTownHall Chat with a few active participants or presenters and many audience or inactive participants
PhenixRoomTypeChannel View only room with a single active member at a time. Broadcast content to many viewers.

PhenixMember

Name Signature Returns Description
getSessionId () NSString The session ID of the member in the room
getObservableRole () Observable(of PhenixMemberRole via NSNumber) Observable of member role
getObservableState () Observable(of PhenixMemberState via NSNumber) Observable of member state
getObservableScreenName () Observable(of NSString) Observable of member screen name
getObservableStreams () Observable(of NSArray of PhenixStream) Observable of array of streams belonging to the member
getObservableLastUpdate () Observable(of NSDate) Observable of utc timestamp
commitChanges (MemberCommitCallback) void Attempts to update server member model with any changes made locally
reload () void Reverts all local changes to the server member model

Member Roles

Role Description
PhenixMemberRoleParticipant Member with streams and is visible to all members
PhenixMemberRoleAudience Member with no streams and is not visible to other members
PhenixMemberRolePresenter Presenter participant member
PhenixMemberRoleModerator Privileged participant member

Member States

Role Description
PhenixMemberStateActive Member is active
PhenixMemberStatePassive Member is not active
PhenixMemberStateHandRaised Member is requesting to become active
PhenixMemberStateInactive Member is not away
PhenixMemberStateOffline Member has left the room

PhenixStream

The stream object may be used to represent any value that will be shared between all members.

Name Signature Returns Description
getUri () NSString The stream uri (may or may not be associated with phenix stream id)
getType () PhenixStreamType The stream type
getObservableAudioState () Observable(of PhenixTrackState via NSNumber) Observable of stream video state
getObservableVideoState () Observable(of PhenixTrackState via NSNumber) Observable of stream audio state

Stream Types

Type Description
PhenixStreamTypeUser General purpose stream
PhenixStreamTypePresentation Privileged stream
PhenixStreamTypeAudio Audio-only stream

Stream Track States

State Description
PhenixTrackStateEnabled Track is enabled
PhenixTrackStateDisabled Track is disabled
PhenixTrackStateEnded Track has ended

Chat Service

The Chat Service provides the ability to:

All room members that are subscribed to the chat will receive the text messages. You must first enter the room before you may send and receive messages.

Initializing

First, instantiate the chat service. A Room Service is required to obtain the chat service.

import PhenixSdk

let roomService: PhenixRoomService = ...  // previously obtained

let chatService = PhenixRoomChatServiceFactory.createRoomChatService(roomService)!


// Use a custom batch size, i.e. max number of message returned by 'getObservableChatMessages'
let batchSize: UInt = 10
let chatServiceWithBatchSize = PhenixRoomChatServiceFactory.createRoomChatService(roomService, batchSize)!

PhenixRoomChatService

Name Signature Returns Description
getObservableChatMessages () Observable(of NSArray of PhenixChatMessage) Get the observable of the most recent chat messages. This is a buffer of the most recent N messages up to a max of 100. See Listen for Chat Messages
getObservableChatEnabled () Observable(of bool via NSNumber) Get the observable of the enabled status of the chat service. Use this to disable or enable the chat.
sendMessageToRoom (message, callback) void Send a message to room.
getMessages (batchSize, afterMessageId, beforeMessageId, callback) void Get chat message history

PhenixChatMessage

Name Type Description
getMessageId NSString Unique ID of message
getObservableTimeStamp Observable(of NSDate) Server UTC timestamp of message
getObservableFrom Observable(of PhenixChatUser) Information on the member that sent the message
getObservableMessage Observable(of NSString) Chat message

PhenixChatUser

Name Type Description
getSessionId NSString Member’s session ID
getObservableScreenName Observable(of NSString) Screen name of the member
getObservableMemberRole Observable(of PhenixMemberRole) Member’s role. See Member Roles
getObservableLastUpdate Observable(of NSDate) Last time member was updated as UTC timestamp

Listen for Chat Messages in the Active Room

The chat service exposes an observable of the most recent N (batchSize) messages in the active room up to a max of 100 messages. To view older messages you will need to use the getMessages method.

import PhenixSdk

let roomService: PhenixRoomService = ...  // previously obtained

let batchSize: UInt = 10
let chatService = PhenixRoomChatServiceFactory.createRoomChatService(roomService, batchSize)!

let chatMessageObservable = chatService.getObservableChatMessages();

var subscription = chatMessageObservable?.subscribe({ change in
    if let change = change {
      let messages = change.value as! [PhenixChatMessage]
      // Do something with chat messages
    }
  })

Send a Message to the Active Room

Send a single message to the active room. If the message is successfully sent the chatMessageObservable will trigger notifications on all subscribers with the new chat message.

import PhenixSdk

let chatService: PhenixRoomChatService = ...  // previously obtained

// Without callback:
chatService.sendMessage(toRoom: "My First Message")

// With callback
chatService.sendMessage(toRoom: "My First Message") { (status: PhenixRequestStatus, message: String?) in
  if status != .ok {
    // handle error - send message again
  }
}

Parameters

Name Type Description
message (required) String Message to send to the room
sendMessageCallback (optional) Function Callback with the status of the request

Send Message Callback Arguments

Name Type Description
status PhenixRequestStatus The status of the operation
message NSString Optional additional error status in case of a failure

Send Message Callback Status Codes

Status Description
ok Send message succeeded
<varies> Send message failed for other reasons

Chat Message History

Get messages older than the most recent N buffered in the chatMessageObservable.

Get the 10 most recent messages after a message:

import PhenixSdk

let chatService: PhenixRoomChatService = ...  // previously obtained

let batchSize: UInt32 = 10
let afterMessageId = "MessageId1"
let beforeMessageId: String? = nil

chatService.getMessages(batchSize, afterMessageId, beforeMessageId) {
    (chatService: PhenixRoomChatService?, messages: [PhenixChatMessage]?, status: PhenixRequestStatus) in
  if status == .ok, let messages = messages {
    // Successfully got chat messages
  } else {
    // handle error - request messages again
  }
}

Get all messages between two messages:

import PhenixSdk

let chatService: PhenixRoomChatService = ...  // previously obtained

let batchSize: UInt32 = 100
let afterMessageId = "MessageId1"
let beforeMessageId = "MessageId2"

chatService.getMessages(batchSize, afterMessageId, beforeMessageId) {
    (roomChatService: PhenixRoomChatService?, messages: [PhenixChatMessage]?, status: PhenixRequestStatus) in
  if status == .ok, let messages = messages {
    // Successfully got chat messages
  } else {
    // handle error - request messages again
  }
}

Parameters

Name Type Description
batchSize (required) UInt32 Limit of messages to receive.
afterMessageId (optional) NSString The messageId to return after. The N messages after message with ID of afterMessageId but before beforeMessageId
beforeMessageId (optional) NSString The messageId to return before. The N messages before message with ID of beforeMessageId
callback (required) Function Callback with the status of the request

Get Messages Callback Arguments

Name Type Description
roomChatService PhenixRoomChatService The chat service making the callback
messages NSArray of PhenixChatMessage Array of chat messages matching search criteria
status PhenixRequestStatus The status of the operation

Get Messages Callback Status Codes

Status Description
ok Get messages succeeded
<varies> Get messages failed for other reasons

Observable

Observe value changes. Similar to the ReactiveX Behavior Subject.

Important differences for Phenix SDK observables:

  1. There is always an initial value (which can be nil for some objects)
  2. 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;
import PhenixSdk

let observable: PhenixObservable<String> = ...  // previously obtained

var subscription = observable.subscribe({ change in
    if let change = change, let 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];
import PhenixSdk

let roleObservable: PhenixObservable<NSNumber> = ...  // previously obtained
let role = PhenixMemberRole(rawValue: roleObservable.getValue().intValue)

let stringObservable: PhenixObservable<NSString> = ...  // previously obtained
let stringValue: String = stringObservable().getValue() as String

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"];
import PhenixSdk

let roleObservable: PhenixObservable<NSNumber> = ...  // previously obtained
roleObservable.setValue(NSNumber(value: PhenixMemberRole.presenter.rawValue))

let stringObservable: PhenixObservable<NSString> = ...  // previously obtained
stringObservable.setValue("new value")