> ## 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.

# SwiftUI

> Show video ads from SwiftUI with VideoAdHost: set an ad to show it, nil to dismiss it.

The SDK presents ads with UIKit. `VideoAdHost` is a SwiftUI view that does the bridging: place it over your player, set its `ad`, and it shows. It is part of `KontextVideoSDK`; there is nothing else to add.

## A player screen

Keep the player, the `VideoAds` and the ad on screen in a model the screen owns with `@StateObject`:

```swift theme={null}
@MainActor
final class EpisodeModel: ObservableObject {
    let player: AVPlayer
    let ads: VideoAds
    @Published var adOnScreen: VideoAd?
    private var pauseAd: VideoAd?

    init(episode: Episode) {
        player = AVPlayer(url: episode.url)
        ads = VideoAds(content: episode.content)
        ads.track(player)
        Task { pauseAd = try? await prepare(.pause) }
    }

    func viewerPaused() {
        player.pause()
        adOnScreen = pauseAd           // VideoAdHost shows it
    }

    func adEnded(_ ad: VideoAd, _ reason: VideoAd.EndReason) {
        adOnScreen = nil
        if reason == .resumed { player.play() }
        Task { pauseAd = try? await prepare(.pause) }   // the next one, ready early
    }

    private func prepare(_ format: AdFormat) async throws -> VideoAd {
        let ad = try await ads.format(format)
        try await ad.load()
        return ad
    }
}

struct EpisodeScreen: View {
    @StateObject var model: EpisodeModel

    var body: some View {
        VideoPlayer(player: model.player)
            .overlay(VideoAdHost(ad: model.adOnScreen, onEnd: model.adEnded))
    }
}
```

## `VideoAdHost`

```swift theme={null}
VideoAdHost(
    ad: model.adOnScreen,
    insets: UIEdgeInsets(top: 0, left: 0, bottom: 80, right: 0),   // clear of your own controls
    onShowFailed: { ad, error in },     // could not be shown: nothing was shown, no onEnd follows
    onImpression: { ad in },
    onClick: { ad, click in },
    onEnd: { ad, reason in }            // set your ad back to nil here
)
```

* **Setting the ad shows it.** A loaded ad shows once the host is on screen with a size. Set `nil`, or another ad, and the one shown is dismissed; it is dismissed too when the host leaves the view hierarchy.
* **Every format.** An overlay or pause ad shows where the host is placed. A linear ad is presented full screen from the top view controller; the host never dismisses it: it ends by itself or by the viewer, and `onEnd` still arrives.
* **The host is the ad's delegate.** Hear the ad through the host's closures, not `ad.delegate`.
* **`onShowFailed`** gets the reason `show` would throw: `anotherAdShowing`, `contentPaused`, `containerTooSmall`, …
* **Taps outside the ad** go through to your player, so your own gestures keep working.

## Keep the ads on the model

SwiftUI recreates view structs. Keep the `VideoAds` on your `@StateObject` model, not in the view: releasing a `VideoAds` ends its ads. Call `ads.destroy()` when the viewer actually leaves the episode (your back button, the next episode), not in `onDisappear`: a linear ad covers your view, and SwiftUI calls `onDisappear` for that too.

## In a feed

Give each page its own model and host, and set the ad on the current page only. An ad on a page swiped away ends within about a second (`.interrupted`), freeing the screen for the next page's ad. See [Ads in a feed](https://docs.kontext.so/video/sdk/swift/guides/feeds.md).


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