iOS SDK Release Notes - PhenixRTS Docs

Release Notes

The most current release of the Apple (iOS) SDK is 2025.0.0.

Release notes for this version are shown below, followed by notes from prior releases.

To get the iOS SDK for a specific version, update the Phenix SDK version Podfile to match the version number followed by '-beta', e.g., 2021.0.15-beta, or contact Phenix for the iOS SDK download location.

Some versions require additional integration steps; these are called out in the version-specific release notes.

v2025.0.0 Release Notes

Release Date: 2025.01.22

Features/Improvements

Updated to reflect the latest Phenix guidance on providing a preview during publishing. Preview is primarily needed before the content is published. This means that, although a preview renderer exists within prior versions of the SDK (withPreviewRenderOptions), the preview functionality should be managed by the application.

v2024.4.0 Release Notes

Release Date: 2024.10.30

Features/Improvements

Enhancements

Fixes/Updates

Deprecations

None.

v2022.0.5 Release Notes

Release Date: 2022.12.02

Features/Improvements

Fixes/Updates

v2022.0.3 Release Notes

Release Date: 2022.07.27

Features/Improvements

Fixes

v2022.0.3 Notes

CDN Domain support

The SDK can now send HTTP requests to URLs that redirect to non-Phenix CDNs for replays or for VOD playlists. To use alternate CDN domains, use the capability cdn-domain on viewing channels, with the CDN domain as its value. Do not include the protocol prefix (e.g., https://) in the value. For example: cdn-domain=mycdn.mycompany.com The platform will replace the default streaming domain with the specified CDN domain in playlist URLs for the primary manifest of the content.

PCast™ URI in tokens

A PCast™ URI contained in the token will now be used instead of a URI passed as a parameter. Putting the URI in the token is now the preferred behavior, and passing in a PCast™ URI as a parameter is now deprecated. To include the URI in the token, add the uri capability as described in the EdgeAuth library documentation. If the token contains a URI that differs from the one passed as a parameter, the URI from the token is used and a warning is written to the log.

Room/Channel ID/Alias in tokens

A room/channel ID or alias passed in the token will be used to join the respective room or channel. As with the PCast™ URI, putting the Room or Channel ID or Alias in the token is now the preferred behavior, and passing in that information as a parameter is now deprecated and will generate a deprecation warning in the log. To include the Room or Channel information in the token, add the roomId, channelId, roomAlias or channelAlias capability as described in the EdgeAuth library documentation. If a value is provided both as a direct parameter and in the token, the values must match; otherwise the operation will fail.

Improved network usage

When using previous releases, Room members rejoining after network changes would sometimes cause zombie room members. This has been improved and the behavior in case of network changes is more consistent. In addition, network usage is reduced when downloading stream replay segments.

Fixed chat room messages loss in case of network issues

Previously, any messages still cached by the SDK during reconnection after a network change were silently discarded. Now they are preserved and will be returned by the next call to GetLastMessages. In addition, an issue has been fixed where new messages were discarded instead of old ones when there were many cached and incoming messages. Upon lost network connectivity, the SDK will now keep trying to reconnect instead of ceasing to retry after a few minutes.

v2021.0.15 Pre-release Notes

Release Date: 2021.08.18

Features/Improvements

No customer-impacting changes.

v2021.0.14 Pre-release Notes

Release Date: 2021.08.09

Features/Improvements

No customer-impacting changes.

v2021.0.11 Pre-release Notes

Release Date: 2021.06.08

Features/Improvements

Fixes

v2021.0.8 Pre-release Notes

Release Date: 2021.05.14

Features/Improvements

v2021.0.8 Integration Notes

PhenixUserMediaStream on iOS.

v2021.0.7 Pre-release Notes

Release Date: 2021.05.06

Features/Improvements

Issues addressed in this release

v2021.0.5 Pre-release Notes

Release Date: 2021.04.12

Features/Improvements

Issues addressed in this release

v2021.0.2 Pre-release Notes

Release Date: 2021.03.02

Features/Improvements

Issues addressed in this release

v2021.0.0 Pre-release Notes

Release Date: 2021.01.07

Features/Improvements

Issues addressed in this release

v2020.1.76 Pre-release Notes

Release Date: 2020.12.22

Features/Improvements

Issues addressed in this release

Notes on Integrating the iOS SDK

// The current implementation of `PhenixTimeShift` leverages HLS chunks.
// It is possible that retrieval of such chunks can take a very long time.
// This will impact playback if the playback head catches up with the end
// of the prefetch window, which will lead to a stall.
// By default, a timeout of 10 seconds for each chunk retrieval is used
// to avoid holding off playback for too long (worst case a chunk will be
// skipped and playback may pause temporarily).
// This timeout is configurable by setting the following option (it defaults
// to 0, which indicates that we should use the internal default):
let timeout: TimeInterval = 5
rendererOptions.timeShiftOptions.chunkRetrievalTimeout = timeout

v2020.1.75 Pre-release Notes

Release Date: 2020.12.11

Features/Improvements

Issues addressed in this release

Notes on Integrating the iOS SDK

import PhenixSdk

// Renderer has been obtained from `PhenixMediaStream` or
// `PhenixExpressSubscriber`:
var renderer: PhenixRenderer = ...

if (!renderer.isSeekable) {
    // This stream does not support time shifting
    return;
}

// Must be in UTC:
let timePoint = Date(...)

var timeShift = renderer.seek(timePoint)

// Observe ready status and call `loop` once PhenixTimeShift is ready.
// This ensures that when `loop` is called playback will start
// instantly.

// All observables return a `PhenixDisposable` object. This needs to be stored
// in order to keep the observable subscription alive.
// It is recommended to make this a member of the enclosing class
let readyDisposable = timeShift?.
    getObservableReadyForPlaybackStatus()?.subscribe { (isReadyChange) in
        if (isReadyChange?.value.boolValue) {
          let loopDuration = TimeInterval(30.0)

timeShift?.loop(loopDuration)
        }
    }

// When time shifted viewing is no longer desired:

// Dispose the `PhenixTimeShift` object.
timeShift = nil  // <-- Playback will jump back to real-time head

// It is also possible to stop a timeshift in progress so it can
// be used again by calling `loop` later
timeShift?.stop()  // <-- Playback will jump back to real-time head

// After `stop()` the ready status will (briefly) switch to false, then
// back to true when enough data has been re-cached.

// `PhenixTimeShift` objects can fail, either during creating when passing in
// an invalid timestamp to `PhenixRenderer.seek()` or any of the other
// `PhenixTimeShift` method. Once a `PhenixTimeShift` has failed, it should be
// dropped and a new one created.
let failureDisposable = timeShift?.
    getObservableFailure()?.subscribe { (failureChange) in
        let errorStatus: PhenixRequestStatus = (failureChange?.value.status)!
        // a non-ok `PhenixRequestStatus` is returned here
    }

// Pause playback
timeShift?.pause()

// Resume playback after pause
timeShift?.play()

// The current playback head can be observed. Each video frame will trigger
// the observable. The head is reported as a `NSDate` object and represents
// UTC.
let playbackHeadDisposable = timeShift?.
    getObservablePlaybackHead()?.subscribe { (playbackHeadChange) in
        let currentPlaybackHead: NSDate = (playbackHeadChange?.value!)!
        // Avoid blocking this thread and dispatch UI updates as needed
    }

// `PhenixTimeShift` supports seeking to any location within the current
// segment.
// The segment is defined by:
// Start: Time point passed into `PhenixRenderer.seek`
// End  : Start plus duration passed into `PhenixTimeShift.loop`

// Seeking is only possible if the `PhenixTimeShift` is either stopped or
// paused.
// Once `PhenixTimeShift` is ready for playback, the observable returned by
// `seek` will return a status code. Call `play()` to start/resume playback if
// the status is OK.
let relativeSeekTime: TimeInterval = -10.0  // Seeks back 10 seconds

let seekDisposable = timeShift?.
    seekRelative(relativeSeekTime)?.subscribe { (seekChange) in
        let status: PhenixRequestStatus = (seekChange?.value.status)!;
            if (status == .ok) {
                timeShift?.play()
            } else {
                // Handle error. In most cases the seek argument would have
                // led to a location outside of the segment
            }
        }

// There is also a second version of seek available named `seekAbsolute`,
// which accepts a `Date` as its argument representing an absolute time point
// in UTC (also has to fall within the boundaries of the segment).

// A `PhenixTimeShift` stream generally consists of several so called layers.
// Layers have different bitrates, usually by varying resolution and framerate
// (lower bitrate layers tend to have lower resolutions and/or framerates).

// While a `PhenixTimeShift` is playing back content, it will automatically
// select an appropriate layer based on the pixel resolution of the rendering
// layer. The logic attempts to select a layer, whose resolution matches
// that of the rendering layer as closely as possible.

// Sometimes an app may want to further restrict the playback layer in order
// to reduce battery usage for instance. This is possible via the
// `PhenixTimeShift.limitBandwidth` API. It forces the `PhenixTimeShift` to
// only select layers with an average bitrate less or equal to the value
// passed into `limitBandwidth`.
// The limitation will remain in effect for as long as the app holds on to
// the `PhenixDisposable` object returned by the API.
// This API can be called at any time: before and during playback.
let bandwithLimitInBps: UInt64 = 400000

var limitBandwidthDisposable = timeShift?.limitBandwidth(bandwithLimitInBps)

// Set the `PhenixDisposable` object to nil to release the bandwidth limit
limitBandwidthDisposable = nil

// Certain behaviors of `PhenixTimeShift` can be changed by passing in
// custom options when the `PhenixRenderer` is created:
let rendererOptions = PhenixRendererOptions()

// When calling `PhenixRenderer.seek` or `PhenixTimeShift.seek` the
// bandwidth will be automatically limited to shorten the amount of time
// it takes for the `PhenixTimeShift` object to become ready.
// The downside of this approach is that once the playback starts, it will
// do so on a lower bitrate rendition of the stream and it generally takes a
// few seconds before the rendition switches to a higher bitrate.
// This behavior is enabled by default, but can be disabled by setting the
// following option to false:
rendererOptions.timeShiftOptions.lowerBandwidthWhileSeekingEnabled = false

// The size of the viewport will determine which rendition of the stream is
// selected (the rendition whose height in pixels is closest to the current
// height of the viewport). This selection process is continuous, which means
// that as the viewport size changes, so does the selected rendition.
// After a new rendition has been selected, it will not render frames
// instantly. Instead, it will take up to about 5 seconds before it will
// render frames.
// This behavior is enabled by default, but can be disabled by setting the
// following option to false:
rendererOptions.timeShiftOptions.viewportSizeBasedRenditionSelectionEnabled = false

// The options object can be passed in directly to the `createRenderer`
// method:
var subscriber: PhenixExpressSubscriber = ... // previously obtained

let renderer = subscriber.createRenderer(rendererOptions)

// Alternatively, it can be passed via various options builders, for example
// for `joinChannel`:
let joinChannelOptionsBuilder =
   PhenixChannelExpressFactory.createJoinChannelOptionsBuilder()

joinChannelOptionsBuilder.withRendererOptions(rendererOptions)

v2020.1.73 Pre-release Notes

Release Date: 2020.12.01

Features/Improvements

Issues addressed in this release

v2020.1.68 Pre-release Notes

Release Date: 2020.11.12

Features/Improvements

Issues addressed in this release

v2020.1.61 Pre-release Notes

Release Date: 2020.09.22

Features/Improvements

Issues addressed in this release

v2020.1.60 Pre-release Notes

Release Date: 2020.09.16

Features/Improvements

Issues addressed in this release

v2020.1.56 Pre-release Notes

Release Date: 2020.08.31

Features/Improvements

Issues addressed in this release

v2020.1.56 Integration Notes

In order to specify the iOS release in the pod spec

In order to pick up that latest version, you may need to run this pod command:

pod install --repo-update

To change this in the Podfile:

def phenix
  pod 'PhenixSdk', '~> 2020.1.56-beta'
end

In order to subscribe to messages by mime-type

import PhenixSdk
import PhenixRoomChatServiceFactory

var wantedMimeTypes = Set<String>()
wantedMimeTypes.insert("text/javascript")

var chatService = PhenixRoomChatService.createRoomChatService(
    roomService,  // previously obtained
    batchSize,    // previously obtained
    wantedMimeTypes
);

var subscriptionLastChatMessage =
    chatService.getObservableLastChatMessage().subscribe({
    change in
    if let change = change, let chatMessage = change.value {
      assert(
          chatMessage.getObservableMimeType().value == "text/javascript",
          "chatService will only get messages with wanted MIME types"
      )
    }
})

var subscriptionLastChatMessage =
    chatService.getObservableLastChatMessage().subscribe({
    change in
    if let change = change {
        let messages = change.value as! [PhenixChatMessage]
        for message in messages {
            assert(
                chatMessage.getObservableMimeType().value =="text/javascript",
                "chatService will only get messages with wanted MIME types"
            )
        }
    }
})

chatService.getMessages(batchSize, afterMessageId, beforeMessageId) {
    (chatService: PhenixRoomChatService?,
    messages: [PhenixChatMessage]?,
    status: PhenixRequestStatus) in
    if status == .ok, let messages = messages {
        for message in messages {
            assert(
                chatMessage.getObservableMimeType().value =="text/javascript",
                "chatService will only get messages with wanted MIME types"
            )
        }
    }
}

// send message with a specific MIME type
// with just the message, MIME type defaults to “text/plain”:
chatService.sendMessageToRoom("My First Message");

// with message and MIME type, but using default callback
chatService.sendMessageToRoom("My First Message", "text/javascript");

// with message, MIME type, and callback
chatService.sendMessageToRoom(
    "My First Message",
    "text/javascript") {
        (status: PhenixRequestStatus, message: String?) in {
            ...
        }
}

v2020.1.52 Pre-release Notes

Release Date: 2020.08.27

Features/Improvements

Issues addressed in this release

v2020.1.49 Pre-release Notes

Release Date: 2020.08.21

Features/Improvements

Issues addressed in this release

Notes

To take advantage of the new functionality in support of stream restarts on change in network type, it works as follows:

Based on the options initially passed to subscribeToMemberStream, the app can either let the SDK create the renderer, or the app can create a renderer itself.