<!--
{
  "availability" : [
    "iOS: -",
    "iPadOS: -",
    "macCatalyst: -",
    "macOS: -",
    "tvOS: -",
    "visionOS: -",
    "watchOS: -"
  ],
  "documentType" : "symbol",
  "framework" : "AVFoundation",
  "identifier" : "/documentation/AVFoundation/AVPlayer/addBoundaryTimeObserver(forTimes:queue:using:)",
  "metadataVersion" : "0.1.0",
  "role" : "Instance Method",
  "symbol" : {
    "kind" : "Instance Method",
    "modules" : [
      "AVFoundation"
    ],
    "preciseIdentifier" : "c:objc(cs)AVPlayer(im)addBoundaryTimeObserverForTimes:queue:usingBlock:"
  },
  "title" : "addBoundaryTimeObserver(forTimes:queue:using:)"
}
-->

# addBoundaryTimeObserver(forTimes:queue:using:)

Requests the invocation of a block when specified times are traversed during normal playback.

```
nonisolated func addBoundaryTimeObserver(forTimes times: [NSValue], queue: dispatch_queue_t?, using block: @escaping @Sendable () -> Void) -> Any
```

## Parameters

`times`

An array of `NSValue` objects containing <doc://com.apple.documentation/documentation/CoreMedia/CMTime> values that represent the times at which to invoke the callback. The system raises an exception if you pass an empty array.

`queue`

A *serial* queue onto which `block` should be enqueued. Passing a concurrent queue is not supported and will result in undefined behavior.

    If you pass     `nil`    , the main queue is used.

`block`

The block to be invoked when any of the times in `times` is crossed during normal playback.

## Return Value

An opaque object that you pass as the argument to [`removeTimeObserver(_:)`](/documentation/AVFoundation/AVPlayer/removeTimeObserver(_:)) to stop observation.

## Discussion

Boundary times are arbitrary points of interest you define within the media timeline. As these times are traversed during normal playback, the block you provide to this method will be invoked. You must maintain a strong reference to the returned value as long as you want the time observer to be invoked by the player. Each invocation of this method should be paired with a corresponding call to [`removeTimeObserver(_:)`](/documentation/AVFoundation/AVPlayer/removeTimeObserver(_:)).

The player does not guarantee the callback block will always be invoked for each boundary time. If your times are very close together along the timeline (close enough that the execution of the block for one takes longer than the difference between them) or if a seek causes time to jump over one or more boundary times, time observation for any specific boundary time may not occur. The best practice is therefore to implement the callback block so it always performs its necessary calculations based solely on the player’s [`currentTime()`](/documentation/AVFoundation/AVPlayer/currentTime()).

The following example shows how you could define boundary times for each quarter of playback.

```swift
func addBoundaryTimeObserver() {
    var times = [NSValue]()
    // Set initial time to zero
    var currentTime = CMTime.zero
    // Divide the asset's duration into quarters.
    let interval = CMTimeMultiplyByFloat64(asset.duration, multiplier: 0.25)
    
    // Build boundary times at 25%, 50%, 75%, 100%
    while currentTime < asset.duration {
        currentTime = currentTime + interval
        times.append(NSValue(time: currentTime))
    }
    
    // Add time observer. Observe boundary time changes on the main queue.
    timeObserverToken = player.addBoundaryTimeObserver(forTimes: times,
                                                       queue: .main) { [weak self] in
        // Update UI
    }
}
```

> Important:
> Use a `weak` reference to `self` in the callback block to prevent creating a retain cycle.

---

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)