> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://fastpix.com/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://fastpix.com/docs/_mcp/server.

# Preload and precache videos

> Preload and precache upcoming playlist items in the FastPix iOS Player to start the next video instantly.

Preload and precache upcoming playlist items in the FastPix iOS Player to start the next video instantly.

The FastPix iOS Player SDK can prepare upcoming playlist items ahead of time, so a viewer who advances to the next video sees little or no buffering. It offers two complementary mechanisms:

* **Preloading** initializes `AVPlayerItem` instances for upcoming videos in the background using a shadow `AVPlayer`. This warms up AVFoundation's URL session cache, so when the SDK loads the same stream URL, the initial buffering is already complete.
* **Precaching** downloads and stores HLS segments to disk, so the content is served from the local cache on later playback requests. DRM-protected items (those with a `drmToken`) are skipped during precaching, because their segments are encrypted and can't be cached.

Both managers expose delegate callbacks, so your app can react to preload and cache state changes in the UI.

\


## Before you begin

Make sure you have the following:

* The FastPix iOS Player SDK installed in your project. See [Install FastPix iOS player](/docs/ios-player/install-fastpix-ios-player).
* A playlist added to the player. See [Manage playlists](/docs/ios-player/manage-playlists).

\


## How preloading and precaching differ

|                     | Preloading                                                   | Precaching                                   |
| ------------------- | ------------------------------------------------------------ | -------------------------------------------- |
| What it does        | Warms AVFoundation's buffer using a shadow `AVPlayer`        | Downloads HLS segments to disk               |
| Manager             | `PreloadManager.shared`                                      | `PrecacheManager.shared`                     |
| DRM-protected items | Supported. AVFoundation handles license fetching separately. | Skipped. Encrypted segments can't be cached. |
| Best for            | An instant start on the next item                            | Serving repeat playback from a local cache   |

\


## Set up the managers

Initialize both managers and assign their delegates before playback starts. Start preloading and precaching once the playlist is ready.

```swift
private let preloadManager = PreloadManager.shared
private let precacheManager = PrecacheManager.shared

override func viewDidLoad() {
    super.viewDidLoad()
    playerViewController.addPlaylist(playlist)

    // Assign delegates to receive status callbacks
    preloadManager.delegate = self
    precacheManager.delegate = self

    // Start preloading and precaching after the playlist is ready
    preloadNextVideos()
    precacheUpcomingVideos()
}
```

\


## Preload upcoming videos

Preload the next items after the currently playing index. This example preloads the next two items, and you can change how many to preload. Call this method whenever the playlist position changes, such as after `next()`, `previous()`, `jumpTo()`, or a `FastPixPlaylistStateChanged` notification.

`buildPlaybackURL(for:)` is your own helper that builds the stream URL for a playlist item, using the same URL you'd use for playback.

```swift
private func preloadNextVideos() {
    let currentIndex = playerViewController.currentPlaylistIndex
    let currentId = currentIndex < playlist.count ? playlist[currentIndex].playbackId : ""
    let upcoming = playlist
        .enumerated()
        .filter { $0.offset > currentIndex }
        .prefix(2)
        .map { $0.element }
        .filter {
            // Skip if already loading, ready, or currently playing
            let status = preloadManager.preloadStatus(forVideo: $0.playbackId)
            switch status {
            case .idle: return $0.playbackId != currentId
            default:    return false
            }
        }

    let itemsToPreload: [(id: String, item: AVPlayerItem)] = upcoming.compactMap { playlistItem -> (id: String, item: AVPlayerItem)? in
        guard let url = buildPlaybackURL(for: playlistItem) else { return nil }
        let playerItem = AVPlayerItem(url: url)
        return (id: playlistItem.playbackId, item: playerItem)
    }

    guard !itemsToPreload.isEmpty else { return }
    preloadManager.preload(items: itemsToPreload)
}
```

\


## Precache upcoming videos

Precache the HLS segments for the current and next video. DRM-protected items are skipped automatically.

```swift
private func precacheUpcomingVideos() {
    let currentIndex = playerViewController.currentPlaylistIndex
    let upcoming = playlist
        .enumerated()
        .filter { $0.offset >= currentIndex }
        .prefix(2)
        .map { $0.element }

    for item in upcoming {
        // Skip DRM-protected content. Encrypted segments cannot be cached.
        guard item.drmToken.isEmpty else { continue }

        let host = item.customDomain.isEmpty == false
            ? item.customDomain
            : "stream.fastpix.io"
        var urlString = "https://\(host)/\(item.playbackId).m3u8"
        if !item.token.isEmpty {
            urlString += "?token=\(item.token)"
        }

        guard let url = URL(string: urlString) else { continue }
        precacheManager.startPrecaching(url: url)
    }
}
```

\


## Consume a preloaded item before navigating

When you move to the next item, call `consumePreloadedItem(for:)` before you call `next()`. This detaches the shadow player, so the SDK can reuse AVFoundation's URL session cache when it loads the same stream URL.

```swift
let nextIndex = playerViewController.currentPlaylistIndex + 1
if nextIndex < playlist.count {
    let nextId = playlist[nextIndex].playbackId
    if preloadManager.consumePreloadedItem(for: nextId) != nil {
        print("Preloaded item consumed. AVFoundation cache warm for: \(nextId)")
    }
}
_ = playerViewController.next()
```

\


## Re-trigger after playlist changes

Re-trigger preloading and precaching inside the `FastPixPlaylistStateChanged` observer, so the window stays ahead of the current position.

```swift
@objc private func playlistStateChanged(_ notification: Notification) {
    DispatchQueue.main.async {
        // ... your existing reset logic ...
        self.preloadNextVideos()
        self.precacheUpcomingVideos()
    }
}
```

\


## Clean up

Stop all in-flight preload and precache tasks when the view controller is deallocated.

```swift
deinit {
    preloadManager.clearAll()
    precacheManager.stopAllPrecaching()
}
```

\


## Handle preload events

Conform to `PreloadManagerDelegate` to receive preload lifecycle callbacks.

```swift
extension VideoPlayerViewController: PreloadManagerDelegate {
    // Called when preloading begins for a video
    func videoPreloadDidStart(forId id: String) {
        print("Preload started for: \(id)")
    }

    // Called when the preloaded AVPlayerItem is buffered and ready
    func videoPreloadDidBecomeReady(forId id: String) {
        print("Preload ready. Instant playback available for: \(id)")
    }

    // Called when preloading fails
    func videoPreloadDidFail(forId id: String, error: Error?) {
        print("Preload failed for \(id): \(error?.localizedDescription ?? "unknown error")")
    }

    // Called when a preload task is cancelled
    func videoPreloadDidCancel(forId id: String) {
        print("Preload cancelled for: \(id)")
    }

    // Called when the SDK auto-advances and consumes the preloaded buffer
    func videoPreloadDidAutoAdvance(toId id: String) {
        print("Auto-advance consumed preloaded item: \(id)")
    }
}
```

\


## Handle precache events

Conform to `PrecacheManagerDelegate` to observe whether segments are served from disk or fetched from the network.

```swift
extension VideoPlayerViewController: PrecacheManagerDelegate {
    // Called when a request is served from the local disk cache
    func videoCacheDidHit(url: URL) {
        print("Cache HIT. Served from disk: \(url.lastPathComponent)")
    }

    // Called when a request is not in the cache and is being downloaded
    func videoCacheDidMiss(url: URL) {
        print("Cache MISS. Downloading and caching: \(url.lastPathComponent)")
    }
}
```

\


## Best practices

* Call `preloadNextVideos()` and `precacheUpcomingVideos()` after every playlist navigation event (`next()`, `previous()`, `jumpTo()`), and inside `playlistStateChanged`, to keep the preload window current.
* DRM-protected items (where `drmToken` is non-empty) are excluded from precaching automatically. Preloading still applies to DRM items, because AVFoundation handles license fetching separately.
* Always call `consumePreloadedItem(for:)` before `next()`, to hand off the buffered data to AVFoundation's URL session cache.
* Call `preloadManager.clearAll()` and `precacheManager.stopAllPrecaching()` in `deinit`, to avoid memory leaks and dangling background tasks.

\


#### Frequently asked questions

Preloading warms AVFoundation's buffer for an upcoming item using a background shadow `AVPlayer`, so the next video starts instantly. Precaching downloads the HLS segments to disk, so later playback of the same item is served from the local cache.

Preloading works with DRM items, because AVFoundation handles license fetching separately. Precaching skips DRM items automatically, because their segments are encrypted and can't be cached.

The examples preload and precache the next two items, which balances instant navigation against bandwidth and storage. Adjust the `prefix(2)` count to preload more or fewer items.

Call `preloadNextVideos()` and `precacheUpcomingVideos()` after every navigation event (`next()`, `previous()`, `jumpTo()`) and inside the `FastPixPlaylistStateChanged` observer, so the window stays ahead of the current position.

Call `preloadManager.clearAll()` and `precacheManager.stopAllPrecaching()` in `deinit` to stop all in-flight tasks and release resources.

\


## What's next

* [Manage playlists](/docs/ios-player/manage-playlists) to set up and navigate multiple videos.
* [Handle network changes](/docs/ios-player/handle-network-changes) to keep playback stable across connectivity changes.
* [Install FastPix iOS player](/docs/ios-player/install-fastpix-ios-player) if you haven't set up the SDK yet.