---
updatedAt: 2026-02-04T10:16:28.000Z
agentTools:
  siteIndex: https://developer.bitmovin.com/llms.txt
  projectIndex: https://developer.bitmovin.com/playback/llms.txt
---

# Offline Playback

This tutorial will walk you through the facilities that the Bitmovin Player iOS SDK offers for working with offline content and offline playback.

## Manage Offline Content For Playback

One of the challenges of enabling offline playback is downloading and managing assets. The Bitmovin SDK provides the ability to download content, as well as to pause, resume and cancel downloads. It also enables downloaded content to be deleted. These operations are provided via the `OfflineContentManager` API, which is responsible for managing the state of the assets. Application lifecycle events such as the application going into background, being terminated by the user, or crashing are handled by the `OfflineManager`.

## Initialize the OfflineManager

You first need to initialize the singleton class `OfflineManager` in your `AppDelegate` class. To do so, you add the following code snippet in the `application(_:didFinishLaunchingWithOptions:)` method:

```swift
OfflineManager.initializeOfflineManager()
```

## App Lifecycle Support

In order to appropriately handle application lifecycle events, such as the application going into background, add the following code into `application(_:handleEventsForBackgroundURLSession:completionHandler:)` in your `AppDelegate`:

```swift
OfflineManager.sharedInstance().add(completionHandler: completionHandler, for: identifier)
```

## Download protected or unprotected content

Before you can download the content, you first need to create an instance of a `SourceConfig `that represents the asset to be downloaded.

```swift
let sourceConfig = SourceConfig(url: URL(string: "https://www.example.com/stream.m3u8")!)
sourceConfig.title = "Sample Content"
sourceConfig.sourceDescription = "Description of this content"
```

### Configure FairPlay protected content

In case the source is protected by FairPlay Digital Rights Management (DRM), you just need to provide its DRM configuration as you would do for online playback:

```swift
let fpsConfig = FairplayConfig(license: licenseUrl, certificateURL: certificateUrl)  
sourceConfig.drmConfig = fpsConfig
```

### Configure ClearKey protected content

When downloading content encrypted with ClearKey (AES-128), the SDK automatically scans for ClearKey decryption keys and stores them for offline playback. This behavior is controlled by the [`supportClearKeyContent`](https://cdn.bitmovin.com/player/ios/3/documentation/bitmovinplayercore/downloadconfig/supportclearkeycontent) property in [`DownloadConfig`](https://cdn.bitmovin.com/player/ios/3/documentation/bitmovinplayercore/downloadconfig), which defaults to `true` as of version `3.91.0`.

### Versions `3.90.1` and below

The default value of `DownloadConfig.supportClearKeyContent` is `false`, therefore to ensure ClearKey decryption keys get downloaded, update `DownloadConfig` before calling `offlineContentManager.download(downloadConfig: downloadConfig)`:

```swift
let downloadConfig = DownloadConfig()
downloadConfig.supportClearKeyContent = true
```

### Starting the download

You will also need a reference to the `OfflineManager` using the `sharedInstance()` method, which you can then use to get access to an `OfflineContentManager` for your `SourceConfig`. The `OfflineContentManager` instance manages offline content and offline DRM related tasks for this single `SourceConfig`:

```swift
let offlineManager = OfflineManager.sharedInstance()  
do {  
    let offlineContentManager = try offlineManager.offlineContentManager(for: sourceConfig)  
} catch {  
    print("error creating offline content manager \(error)")  
}
```

Now that you have both the offline content manager and source config for the asset, you can already kick off the download operation, by calling the `download()` method.

```swift
offlineContentManager.download()
// or when using DownloadConfig
offlineContentManager.download(downloadConfig: downloadConfig)
```

## OfflineState

You can check the state of the download operation with the `offlineState` property, which is very useful for checking on the state of an asset before performing further actions. For example, checking the asset's offline state before updating the UI or allowing the user to cancel a download:

```swift
switch offlineContentManager.offlineState {  
    case .downloading:  
        print("[MyViewController] asset download is in progress")  
    case .suspended:  
        print("[MyViewController] asset download is suspended")  
    case .downloaded:  
        print("[MyViewController] asset download is finished")  
    case .canceling:  
        print("[MyViewController] canceling asset download")  
    case .notDownloaded:  
        print("[MyViewController] asset is not downloaded")  
    @unknown default:  
        break  
}
```

## Events

Additionally, the SDK allows you to add event listeners for tracking all the offline states mentioned above.

```swift
offlineContentManager.add(listener: self)
```

And then you can track the events for further action:

```swift
extension MyViewController: OfflineContentManagerListener {
    func onOfflineError(
        _ event: OfflineErrorEvent, 
        offlineContentManager: OfflineContentManager
    ) {
        let errorMessage = event.message
        print("[MyViewController] Download resulted in error: \(errorMessage)")
    }
  
    func onContentDownloadFinished(
        _ event: ContentDownloadFinishedEvent,
        offlineContentManager: OfflineContentManager
    ) {
        print("[MyViewController] Download Finished")
    }
  
    func onContentDownloadProgressChanged(
        _ event: ContentDownloadProgressChangedEvent, 
        offlineContentManager: OfflineContentManager
    ) {
        print("[MyViewController] Progress")
    }
  
    func onContentDownloadSuspended(
        _ event: ContentDownloadSuspendedEvent,
        offlineContentManager: OfflineContentManager
    ) {
        print("[MyViewController] Suspended")
    }
  
    func onContentDownloadResumed(
        _ event: ContentDownloadResumedEvent,
        offlineContentManager: OfflineContentManager
    ) {
        print("[MyViewController] Resumed")
    }
  
    func onContentDownloadCanceled(
        _ event: ContentDownloadCanceledEvent,
        offlineContentManager: OfflineContentManager
    ) {
        print("[MyViewController] Canceled")
    }
}
```

You can find more information about each of the above events in our documentation:

* `onOfflineError`
* `onContentDownloadFinished`
* `onContentDownloadProgressChanged`
* `onContentDownloadSuspended`
* `onContentDownloadResumed`
* `onContentDownloadCanceled`

## Cancel an ongoing download

The `OfflineContentManager` offers the `cancelDownload()` method that cancels a running or suspended download operation. When a download was canceled successfully, also all partially downloaded data is deleted automatically.

```swift
guard offlineContentManager.offlineState == .downloading ||
      offlineContentManager.offlineState == .suspended else {
    return
}
offlineContentManager.cancelDownload()
```

## Suspend an ongoing download

The `suspendDownload()` method can be used to suspend (pause) an ongoing download operation.

```swift
guard offlineContentManager.offlineState == .downloading else {  
    return  
}  
offlineContentManager.suspendDownload()
```

## Resume a suspended download

If the download of a given source config has `.suspended state`, it can be resumed at any time with the `resumeDownload()` method:

```swift
guard offlineContentManager.offlineState == .suspended else {  
    return  
}  
offlineContentManager.resumeDownload()
```

## Delete all downloaded data

You can force to delete all cached data for a given source config with the `deleteOfflineData()` method:

```swift
guard offlineContentManager.offlineState == .downloaded else {  
    return  
}  
offlineContentManager.deleteOfflineData()
```

## Overview of states and allowed method calls

The following table outlines all allowed actions in all possible offline states a source config can have. It also shows which listener methods are called on the `OfflineContentManagerListener`:

| Current State | Method call triggering transition | OfflineContentManagerListener Event | Following State |
| :------------ | :-------------------------------- | :---------------------------------- | :-------------- |
| NotDownloaded | download                          |                                     | Downloading     |
| Downloading   | cancelDownload                    |                                     | Canceling       |
| Downloading   | suspendDownload                   | onContentDownloadSuspended          | Suspended       |
| Downloading   |                                   | onContentDownloadFinished           | Downloaded      |
| Downloading   |                                   | onContentDownloadProgressChanged    | Downloading     |
| Downloaded    | deleteOfflineData                 |                                     | NotDownloaded   |
| Suspended     | resumeDownload                    | onContentDownloadResumed            | Downloading     |
| Suspended     | cancelDownload                    |                                     | Canceling       |
| Canceling     |                                   | onContentDownloadCanceled           | NotDownloaded   |
| Downloaded    | renewOfflineLicense               | onOfflineContentLicenseRenewed      | Downloaded      |

## Play online/offline content seamlessly

Now that you know how to manage assets for offline playback, you can focus on the what is needed to play back the asset. Assuming the offline state of of the offline content manager for the source config is `.downloaded`, `.downloading `or `.suspended`, before passing the source config to the player, you should check whether the device has access to the internet or not. If the device does not have internet access, the method `offlineContentManager.createOfflineSourceConfig(restrictedToAssetCache:)` can be used to create an offline source config from the initial downloaded source config:

```swift
guard offlineContentManager.offlineState == .downloaded,
      let offlineSourceConfig = offlineContentManager.createOfflineSourceConfig(restrictedToAssetCache: true) else {
    finishWithError(title: "Error", message: "The device seems to be offline, but no offline content for the selected source available.")
    return
}
sourceConfig = offlineSourceConfig
```

This method gives you the option to `restrictToAssetCache`, which will restrict the playback to the audio and subtitle tracks which are cached on disk. As a result, tracks which are not cached, do not show up as selectable in the player UI. If the device has access to the internet, you should probably set `false` to this parameter as it would allow the user to select tracks that are not cached yet. In the case that the offline state for the offline content manager for the source config is `.notDownloaded` or `.canceling`, and the device does not have access to the internet, then the playback will not be possible and you should notify the user. Once these checks are done and playback is possible, you can load the source config into the player:

```swift
let player = PlayerFactory.createPlayer()

player.add(listener: self)
player.load(sourceConfig: sourceConfig) // this can be an instance of `SourceConfig` or `OfflineSourceConfig`
```

## Sample App

[Here](https://github.com/bitmovin/bitmovin-player-ios-samples/) you will find a set of iOS sample applications. There is also a [dedicated app](https://github.com/bitmovin/bitmovin-player-ios-samples/tree/master/BasicOfflinePlayback) that demonstrates the offline playback capabilities.