Migrating from AVPlayer to the Bitmovin Player

In this guide, we will show you how to migrate from AVPlayer to the Bitmovin Player in just two simple steps.

Step 1: Add and configure your Bitmovin Player

You have several options for integrating the Player SDK into your project.

We recommend you use the latest version of our Bitmovin Player. You can find all versions here.

Using CocoaPods

Add the following lines to the Podfile of your project and replace the Version Number with the latest version of the SDK.

+ source 'https://github.com/bitmovin/cocoapod-specs.git'
+ pod 'BitmovinPlayer', 'Version Number'

Using Swift Package Manager

To integrate using Xcode 14.1 or newer, open your project file and specify it in Project > Package Dependencies using the following URL:




You can also add the Player SDK to your project via different ways. Learn how to do that in our Getting Started Guide.

Configure the license key

Add your license key to the <dict> in the Info.plist:

+ <key>BitmovinPlayerLicenseKey</key>
+ <string>PLAYER_LICENSE_KEY</string>

Alternatively, you can also set the license key programmatically on the PlayerConfig via PlayerConfig.key.

Allowlist your Bundle Identifiers

And finally, register the package name of your application in your Bitmovin Dashboard under "Player" -> "Licenses". This security mechanism protects your license from being used elsewhere.

Step 2: Replace AVPlayer references in your project

Replace AVPlayer API

The APIs of AVPlayer and Bitmovin Player are similar in functionality. As a result, the migration is mostly about changing method names and signatures.

Here is an example of creating a new Bitmovin Player:

- import AVFoundation
+ import BitmovinPlayer

- let player = AVPlayer()
+ let player = PlayerFactory.create()
- #import <AVFoundation/AVFoundation.h>
+ #import <BitmovinPlayer/BitmovinPlayer.h>

- AVPlayer *player = [AVPlayer new];
+ id<BMPPlayer> player = [BMPPlayerFactory create];

Next, replace occurrences of AVPlayerItem by SourceConfig and load your sources. Here is an example of loading an HLS stream:

  guard let url = URL(string: "https://cdn.bitmovin.com/content/art-of-motion/m3u8s/playlist.m3u8") else {

- let playerItem = AVPlayerItem(url: url)
- player.replaceCurrentItem(with: playerItem)
+ let sourceConfig = SourceConfig(url: url, type: .hls)
+ player.load(sourceConfig: sourceConfig)
  NSURL *streamUrl = [NSURL URLWithString:@"https://cdn.bitmovin.com/content/art-of-motion/m3u8s/playlist.m3u8"];

- AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:url];
- [player replaceCurrentItemWithPlayerItem:playerItem];
+ BMPSourceConfig *sourceConfig = [[BMPSourceConfig alloc] initWithUrl:url type:BMPSourceTypeHls];
+ [player loadSourceConfig: sourceConfig];
  [player play];

User Interface

To migrate the UI, replace occurrences of AVPlayerViewController with PlayerView.
An example:

- import AVKit
+ import BitmovinPlayer

- let playerViewController = AVPlayerViewController()
- playerViewController.player = player
+ let playerView = PlayerView(player: player, frame: .zero)
- #import <AVKit/AVKit.h>
+ #import <BitmovinPlayer/BitmovinPlayer.h>

- AVPlayerViewController *viewController = [AVPlayerViewController new];
- viewController.player = player;
+ BMPPlayerView *playerView = [[BMPPlayerView alloc] initWithPlayer:player frame:CGRectZero];



For full snippet for embedding PlayerView into a UIViewController, see here.

Learn how to customize your cross-platform Bitmovin UI here.



To support you with the further migration, we've created mapping tables between the AVPlayer and Bitmovin Player API, which you can refer to - especially for more advanced use cases.


And that's it!

You're ready to play a video in your application using the Bitmovin Player 😀


In this guide, we demonstrated how easy it is to migrate from AVPlayer to the Bitmovin Player in just two simple steps: adding & configuring the Bitmovin Player and mapping the AVPlayer API to the Bitmovin Player API.

Next, you can

Appendix: Mapping Tables

For more advanced use cases, we've created the following mapping tables to support your migration.
In comparison, the Bitmovin Player API allows more expressive and terse syntax by using event based communication whereas AVPlayer makes heavy use of Key-Value Observing and Notifications.

AVPlayer ➡️ Bitmovin Player mapping

AVPlayerBitmovin Player
AVPlayer.replaceCurrentItem(with: nil)Player.unload()
AVPlayer.currentItem.tracksPlayer.availableAudio and Player.availableSubtitles
AVPlayer.currentItem.errorPlayerListener.onSourceError and SourceListener.onSourceError
AVPlayer.timeControlStatus when .playingPlayerListener.onPlaying
AVPlayer.timeControlStatus when .pausedPlayerListener.onPaused
AVQueuePlayer.insert(_:, after:)Player.playlist.add(source: at:)

Additional Notes


AVPlayer.seek(to time: CMTime) (seeking in the current source) is equivalent to:

AVQueuePlayer.advanceToNextItem() (seeking in any source of the playlist) is equivalent to:


The Bitmovin Player provides an extensive Event Framework. Listening to a Player event is as simple as:

class EventListener: NSObject, PlayerListener {
    func onReady(_ event: ReadyEvent, player: Player) {
        // player is ready for immediate playback

let listener = EventListener()
player.add(listener: listener)
@interface EventListener : NSObject<PlayerListener>

@implementation EventListener
- (void)onReady:(ReadyEvent *)event player:(Player *)player {
    // player is ready for immediate playback

EventListener *listener = [[EventListener alloc] init];
[player addListener:listener];

Compared to Key-Value Observing based solution with AVPlayer:

class Observer: NSObject {
    override public func observeValue(
        forKeyPath keyPath: String?,
        of object: Any?,
        change: [NSKeyValueChangeKey: Any]?,
        context: UnsafeMutableRawPointer?
    ) {
        switch keyPath {
        case #keyPath(AVPlayer.status):
            handlePlayerStatusChanged(change: change)

    private func handlePlayerStatusChanged(change: [NSKeyValueChangeKey: Any]?) {
        guard let newValue = change?[.newKey] as? Int,
              let newStatus = AVPlayer.Status(rawValue: newValue) else {

        switch newStatus {
        case .readyToPlay:
            // player is ready for immediate playback
let observer = Observer()
player.addObserver(observer, forKeyPath: #keyPath(AVPlayer.status), options: [.new], context: nil)
@interface Observer : NSObject

@implementation Observer
- (void)observeValueForKeyPath:(NSString *)keyPath
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(void *)context {
    if ([keyPath isEqualToString:@"status"]) {
        [self handlePlayerStatusChanged:change];

- (void)handlePlayerStatusChanged:(NSDictionary<NSKeyValueChangeKey,id> *)change {
    NSNumber *newValue = change[NSKeyValueChangeNewKey];
    AVPlayerStatus newStatus = (AVPlayerStatus)[newValue integerValue];
    switch (newStatus) {
        case AVPlayerStatusReadyToPlay:
            // player is ready for immediate playback

Observer *observer = [Observer new];
[player addObserver:observer forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];

For more advanced use cases, take a look at our API documentation for the PlayerListener here.