<!--
{
  "documentType" : "article",
  "framework" : "AVFoundation",
  "identifier" : "/documentation/AVFoundation/controlling-the-transport-behavior-of-a-player",
  "metadataVersion" : "0.1.0",
  "role" : "article",
  "title" : "Controlling the transport behavior of a player"
}
-->

# Controlling the transport behavior of a player

Play, pause, and seek through a media presentation.

## Discussion

AVFoundation provides comprehensive support for playing media assets, including local and remote file-based media and also media streamed with HTTP Live Streaming. The framework models its media assets using the [`AVAsset`](/documentation/AVFoundation/AVAsset) class, which provides a consistent interface to load and inspect your media, regardless of its type or location. Use an [`AVPlayer`](/documentation/AVFoundation/AVPlayer) object to play media assets in the form of [`AVPlayerItem`](/documentation/AVFoundation/AVPlayerItem) objects, which model the dynamic state of an asset such as its [`currentTime()`](/documentation/AVFoundation/AVPlayerItem/currentTime()).

Understanding how to effectively use [`AVPlayer`](/documentation/AVFoundation/AVPlayer) is essential for anyone building a custom player UI or otherwise requiring programmatic control of playback.

### Observe playback readiness

When you create a player item, it starts with a status of [`AVPlayerItem.Status.unknown`](/documentation/AVFoundation/AVPlayerItem/Status-swift.enum/unknown), which means the system hasn’t attempted to load its media for playback. Only when you associate the item with an [`AVPlayer`](/documentation/AVFoundation/AVPlayer) object does the system begin loading an asset’s media.

To know when the player item is ready for playback, observe the value of its [`status`](/documentation/AVFoundation/AVPlayerItem/status-swift.property) property. Add this observation before you call the player’s [`replaceCurrentItem(with:)`](/documentation/AVFoundation/AVPlayer/replaceCurrentItem(with:)) method, because associating the player item with a player is the system’s cue to load the item’s media:

```swift
var statusObservation: NSKeyValueObservation?

func playMedia(at url: URL) {
    let asset = AVAsset(url: url)
    let playerItem = AVPlayerItem(
        asset: asset,
        automaticallyLoadedAssetKeys: [.tracks, .duration, .commonMetadata]
    )
    // Key-value observe the status property before associating with player.
    statusObservation = playerItem.observe(\.status, options: [.new, .initial]) { [weak self] item, _ in
        guard let self else { return }
        Task { @MainActor in
            switch item.status {
            case .readyToPlay:
                // Ready to play. Present playback UI.
            case .failed:
                // A failure while loading media occurred.
            default:
                break
            }
        }
    }
    // Set the item as the player's current item.
    player.replaceCurrentItem(with: playerItem)
}
```

When the player item reaches a [`AVPlayerItem.Status.readyToPlay`](/documentation/AVFoundation/AVPlayerItem/Status-swift.enum/readyToPlay) state, present or enable your playback UI. Alternatively, if a failure occurs, show the appropriate status in the player.

### Control the playback rate

A player provides the [`play()`](/documentation/AVFoundation/AVPlayer/play()) and [`pause()`](/documentation/AVFoundation/AVPlayer/pause()) methods as its primary means of controlling its playback rate. When a player item is ready for playback, call the player’s [`play()`](/documentation/AVFoundation/AVPlayer/play()) method to request that playback begins at the [`defaultRate`](/documentation/AVFoundation/AVPlayer/defaultRate), which has an initial value of `1.0` (the natural rate). By default, a player automatically waits to start playback until it has sufficient media data available to minimize stalling. You can determine whether a player is in a paused, waiting to play, or playing state by observing its [`timeControlStatus`](/documentation/AVFoundation/AVPlayer/timeControlStatus-swift.property) value:

```swift
var isPlaying = false
var timeControlObservation: NSKeyValueObservation?

private func observePlayingState() {
    timeControlObservation = player.observe(\.timeControlStatus, options: [.new, .initial]) { [weak self] player, _ in
        guard let self else { return }
        Task { @MainActor in
            self.isPlaying = player.timeControlStatus == .playing
        }
    }
}
```

Observe changes to the [`rate`](/documentation/AVFoundation/AVPlayer/rate) property by observing notifications of type [`rateDidChangeNotification`](/documentation/AVFoundation/AVPlayer/rateDidChangeNotification). Observing this notification is similar to key-value observing the [`rate`](/documentation/AVFoundation/AVPlayer/rate) property, but provides additional information about the reason for the rate change. Retrieve the reason from the notification’s <doc://com.apple.documentation/documentation/Foundation/Notification/userInfo> dictionary using the [`rateDidChangeReasonKey`](/documentation/AVFoundation/AVPlayer/rateDidChangeReasonKey) constant:

```swift
// Observe changes to the playback rate asynchronously.
private func observeRateChanges() async {
    let name = AVPlayer.rateDidChangeNotification
    for await notification in NotificationCenter.default.notifications(named: name) {
        guard let reason = notification.userInfo?[AVPlayer.rateDidChangeReasonKey] as? AVPlayer.RateDidChangeReason else {
            continue
        }
        switch reason {
        case .appBackgrounded:
            // The app transitioned to the background.
        case .audioSessionInterrupted:
            // The system interrupts the app’s audio session.
        case .setRateCalled:
            // The app set the player’s rate.
        case .setRateFailed:
            // An attempt to change the player’s rate failed.
        default:
            break
        }
    }
}
```

### Seek through the media timeline

You can seek through a media timeline in several ways using the methods of [`AVPlayer`](/documentation/AVFoundation/AVPlayer) and [`AVPlayerItem`](/documentation/AVFoundation/AVPlayerItem). The most common way is to use the player’s [`seek(to:)`](/documentation/AVFoundation/AVPlayer/seek(to:)-87h2r) method, passing it a destination <doc://com.apple.documentation/documentation/CoreMedia/CMTime> value. Call this method in an asynchronous context:

```swift
// Handle time update request from user interface.
func seek(to timeInterval: TimeInterval) async {
    // Create a CMTime value for the passed in time interval.
    let time = CMTime(seconds: timeInterval, preferredTimescale: 600)
    await avPlayer.seek(to: time)
}
```

You can call this method a single time to seek to the location, but you can also call it continuously such as when you use a <doc://com.apple.documentation/documentation/SwiftUI/Slider> view.

The [`seek(to:)`](/documentation/AVFoundation/AVPlayer/seek(to:)-87h2r) method is a convenient way to quickly seek through your presentation, but it’s tuned for speed rather than precision. This means the actual time to which the player seeks may differ slightly from the time you request. If you need to implement precise seeking behavior, use the [`seek(to:toleranceBefore:toleranceAfter:)`](/documentation/AVFoundation/AVPlayer/seek(to:toleranceBefore:toleranceAfter:)) method, which lets you indicate the tolerated amount of deviation from your target time (before and after). For example, if you need to provide sample-accurate seeking behavior, specify tolerance values of zero:

```swift
// Seek precisely to the specified time.
await avPlayer.seek(to: time, toleranceBefore: .zero, toleranceAfter: .zero)
```

---

Copyright &copy; 2026 Apple Inc. All rights reserved. | [Terms of Use](https://www.apple.com/legal/internet-services/terms/site.html) | [Privacy Policy](https://www.apple.com/privacy/privacy-policy)