> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kontext.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Showing your first ad

> Create the video's ads, get an ad, load it early, and show it at the right moment: overlay, pause or linear.

This is the 10-minute integration. Before you start, complete [Installation](https://docs.kontext.so/video/sdk/swift/installation.md) so `KontextVideo.shared.initialize(with:)` runs at launch.

Every format follows the same four steps:

1. `VideoAds(content:)` for the video the viewer is watching.
2. `ads.format()` asks the server and returns a `VideoAd`.
3. `ad.load()` renders the creative off screen, so showing it later is instant. Or `ad.reject()` if you won't use it.
4. `ad.show()` at the moment you choose. One `didEnd` callback when it is over.

## 1. Create the video's ads

One `VideoAds` per video (episode). Creating it sends nothing.

```swift theme={null}
import KontextVideoSDK

let content = KontextVideo.Content(
    id: "episode-1234",
    language: "en",
    title: "Episode 1",
    duration: 93,
    genre: "romance",
    series: .init(id: "series-42", title: "My Marionette Lover", episodeCount: 6)
)
let ads = VideoAds(content: content)
```

Only `id` and `language` are required; everything else improves targeting, so send what you have.

Tell the SDK where the content is playing, so events carry the content playhead:

```swift theme={null}
ads.trackPlayhead(of: player)   // your AVPlayer, once this episode plays in it
```

For a player that is not an `AVPlayer`, set `ads.playheadProvider = { seconds }` instead.

## 2. Get an ad

```swift theme={null}
overlayAd = try await ads.format(.overlay)   // pauseAd with .pause, linearAd with .linear
```

`format()` asks the server for an ad of the format you pass. Ask early, when the episode opens, not at the moment you want to show the ad: the request and the load take a moment, and the ad should appear instantly.

It throws `VideoAdError.noFill(reason:)` when the server has nothing. That is normal, not a failure; ask again later.

## 3. Load it, or reject it

```swift theme={null}
try await overlayAd?.load()
overlayAd?.delegate = self
```

`load()` renders the creative off screen and returns once it is ready, so `show()` is instant. If you decide not to use the ad, call `try overlayAd?.reject()` and the advertiser is told.

## 4. Show it

Each format has its own moment and its own `show()`.

**Overlay**

A strip over the player while the episode plays, at a moment you pick. It goes away by itself after a few seconds.

```swift theme={null}
func showOverlay() {
    try? overlayAd?.show(in: playerView)   // the view your video plays in
}
```

To take it away earlier, call `dismiss()` on it.

**Pause**

A card, or the full player, when the viewer pauses. Take it away when they resume.

```swift theme={null}
func viewerPaused() {
    player.pause()
    try? pauseAd?.show(in: playerView)   // the view your video plays in
}

func viewerResumed() {
    try? pauseAd?.dismiss()
    player.play()
}
```

The viewer can also tap Resume on a full-screen pause ad; you resume playback in `didEnd`.

**Linear**

A full-screen video between, before or after episodes. Pause your content first; the SDK presents the ad itself.

```swift theme={null}
func showLinear() {
    guard let linearAd else { return }
    player.pause()
    do {
        try linearAd.show()              // from the top view controller; or show(from: viewController)
    } catch {
        player.play()                    // it never appeared, so nothing else follows
    }
}
```

You resume your content in `didEnd`.

`show()` either throws, and nothing happened, or the ad is on screen. From then on the SDK runs it until it ends.

## 5. React to the end

Implement `VideoAdDelegate`. Every method has an empty default; the one you need is `didEnd`. It arrives exactly once per shown ad, whoever ended it, so put your resume logic there and nowhere else, and ask for the next ad from there, so it is ready early.

**Overlay**

Your content kept playing, so there is nothing to resume.

```swift theme={null}
extension PlayerController: VideoAdDelegate {
    func videoAd(_ ad: VideoAd, didEnd reason: VideoAd.EndReason) {
        overlayAd = nil
        // prepare the next ad early: format(), then load()
    }
}
```

**Pause**

When the viewer tapped Resume on a full-screen pause ad, resume playback here. When they resumed through your own control, `viewerResumed()` already did.

```swift theme={null}
extension PlayerController: VideoAdDelegate {
    func videoAd(_ ad: VideoAd, didEnd reason: VideoAd.EndReason) {
        pauseAd = nil
        if reason == .resumed { player.play() }        // the viewer tapped Resume on the ad
        // prepare the next ad early: format(), then load()
    }
}
```

**Linear**

Your content paused for the ad; resume it, whatever the reason.

```swift theme={null}
extension PlayerController: VideoAdDelegate {
    func videoAd(_ ad: VideoAd, didEnd reason: VideoAd.EndReason) {
        linearAd = nil
        player.play()
        // prepare the next ad early: format(), then load()
    }
}
```

## 6. Tear down

Call `destroy()` when the viewer leaves the video. It drops every held ad, tells the server which ones were not used, and ends the one on screen.

```swift theme={null}
ads.destroy()
```

## The whole thing

One controller per episode. Pick the tab for your format:

**Overlay**

```swift theme={null}
@MainActor
final class PlayerController: VideoAdDelegate {
    let player: AVPlayer
    let playerView: UIView
    let ads: VideoAds
    var overlayAd: VideoAd?

    init(content: KontextVideo.Content, player: AVPlayer, playerView: UIView) {
        self.player = player
        self.playerView = playerView
        ads = VideoAds(content: content)
        ads.trackPlayhead(of: player)
        Task { await prepare() }
    }

    func prepare() async {
        do {
            let ad = try await ads.format(.overlay)
            try await ad.load()
            ad.delegate = self
            overlayAd = ad
        } catch {
            // no fill this time; ask again at the next moment
        }
    }

    func showOverlay() {                         // a moment you pick
        try? overlayAd?.show(in: playerView)
    }

    func videoAd(_ ad: VideoAd, didEnd reason: VideoAd.EndReason) {
        overlayAd = nil
        Task { await prepare() }
    }

    func leave() {
        ads.destroy()
    }
}
```

**Pause**

```swift theme={null}
@MainActor
final class PlayerController: VideoAdDelegate {
    let player: AVPlayer
    let playerView: UIView
    let ads: VideoAds
    var pauseAd: VideoAd?

    init(content: KontextVideo.Content, player: AVPlayer, playerView: UIView) {
        self.player = player
        self.playerView = playerView
        ads = VideoAds(content: content)
        ads.trackPlayhead(of: player)
        Task { await prepare() }
    }

    func prepare() async {
        do {
            let ad = try await ads.format(.pause)
            try await ad.load()
            ad.delegate = self
            pauseAd = ad
        } catch {
            // no fill this time; ask again at the next moment
        }
    }

    func viewerPaused() {
        player.pause()
        try? pauseAd?.show(in: playerView)
    }

    func viewerResumed() {
        try? pauseAd?.dismiss()
        player.play()
    }

    func videoAd(_ ad: VideoAd, didEnd reason: VideoAd.EndReason) {
        pauseAd = nil
        if reason == .resumed { player.play() }   // the viewer tapped Resume on the ad
        Task { await prepare() }
    }

    func leave() {
        ads.destroy()
    }
}
```

**Linear**

```swift theme={null}
@MainActor
final class PlayerController: VideoAdDelegate {
    let player: AVPlayer
    let playerView: UIView
    let ads: VideoAds
    var linearAd: VideoAd?

    init(content: KontextVideo.Content, player: AVPlayer, playerView: UIView) {
        self.player = player
        self.playerView = playerView
        ads = VideoAds(content: content)
        ads.trackPlayhead(of: player)
        Task { await prepare() }
    }

    func prepare() async {
        do {
            let ad = try await ads.format(.linear)
            try await ad.load()
            ad.delegate = self
            linearAd = ad
        } catch {
            // no fill this time; ask again at the next moment
        }
    }

    func showLinear() {                          // a moment you pick
        guard let linearAd else { return }
        player.pause()
        do { try linearAd.show() } catch { player.play() }
    }

    func videoAd(_ ad: VideoAd, didEnd reason: VideoAd.EndReason) {
        linearAd = nil
        player.play()
        Task { await prepare() }
    }

    func leave() {
        ads.destroy()
    }
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.