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.
Swift SDK
See how easy it is to integrate high-performance ads into your iOS app using our lightweight SDK.
Requirements
- iOS 14.0+
- Swift 5.9+
- Xcode 15+
Getting started
1. Installation
You’ll need a publisher account to obtain your
publisherToken and placement codes.- Swift Package Manager
- CocoaPods
Add the SDK as a package dependency:Then add the
KontextSwiftSDK product to your target.2. Create a session
The entry point isKontextAds.createSession(_:), which returns a Session you’ll use for the rest of the conversation lifecycle.
userId, conversationId, and publisherToken fixed for its lifetime. Recreate the session when any of those change (e.g. when the user starts a new chat).
Session is @MainActor — call its methods from the main actor (default in SwiftUI views and @MainActor-isolated view controllers).3. Set up the IDFA (App Tracking Transparency)
The SDK reads the IDFA when the user has granted ATT permission. Add the usage description toInfo.plist:
ATTrackingManager.requestTrackingAuthorization automatically. To trigger it later (e.g. after onboarding) or manage the prompt yourself:
advertisingId and vendorId are both optional. Leave them unset to let the SDK collect them (IDFA via ATTrackingManager when authorized, IDFV via UIDevice.current.identifierForVendor). Pass them only when you already collect those identifiers yourself.
4. Set up SKAdNetwork
The SDK readsSKAdNetworkItems from Info.plist and forwards them on every /init so DSPs can measure conversions. Append the identifiers provided during onboarding:
5. Feed conversation messages
Add every message to the session as it appears. User messages trigger a debounced preload in the background; assistant messages let the SDK assign the matched bid to the corresponding placement.addMessage returns synchronously. The preload result is delivered later via the onEvent callback (.filled, .noFill, .error, …) — not via a return value.
6. Render the ad
Usesession.createAd(messageId:) to obtain an Ad for an assistant message, then render it with InlineAdUIView.
createAd is idempotent: calling it repeatedly with the same messageId returns the same Ad. Cache the returned instance so view-controller / cell reuse doesn’t recreate it.
7. Tear down
Calldestroy() when the conversation ends or the view disappears. Idempotent and required to cancel pending network requests and release web view resources.
Trying the example app
TheExample directory in the SDK repo ships a working UIKit demo you can run end-to-end against your own publisher token.
Example scheme on a simulator or device.
ExampleSecrets.swift lives at the repo root (not inside Example/) and is gitignored, so your token won’t be committed. The Xcode project references it at ../../ExampleSecrets.swift; if the file is missing the build fails clearly — copy the .example template before opening Xcode.publisherToken placeholder (YOUR_PUBLISHER_TOKEN) won’t return real ads — replace it with your token from the publisher dashboard.
Observing events
Two equivalent ways to consumeAdEvents — pick whichever fits your codebase:
onEvent callback
Combine publisher
Live-updating session options
A subset of session options can be live-updated without recreating the session. Read at the next/preload, so changes take effect on the next user message.
variantId, regulatory, userEmail, advertisingId, vendorId. Non-nil fields overwrite; nil fields are left unchanged. To clear a field, recreate the session.
SwiftUI
The SDK ships UIKit views only. WrapInlineAdUIView in a UIViewRepresentable to embed it in SwiftUI:
API reference
KontextAds.createSession(_:)
Session configured from the given SessionOptions. Fires /init in the background.
SessionOptions
Your unique publisher token.
Stable identifier for the end user. Used for personalization, frequency capping, and rewarded ads.
Unique ID of the current conversation / chat thread.
Placement codes to request ads for. Defaults to
["inlineAd"] when nil or empty.AI character metadata for contextual targeting.
Publisher-defined cohort identifier (e.g. for A/B testing).
Privacy / consent signals. TCF (
gdpr / gdprConsent) is collected automatically if a TCF-compliant CMP is integrated — set manually only for COPPA, GPP, or US Privacy.End-user email for frequency-cap deduplication.
IDFA you collected yourself. Takes priority over the SDK’s automatic collection. Use with
requestTrackingAuthorization: false.IDFV you collected yourself. Falls back to
UIDevice.current.identifierForVendor when nil.Whether the SDK should auto-request ATT authorization. Defaults to
true.Callback invoked on every ad lifecycle event. Called on the main thread.
Session
Append a When
Message to the conversation. Synchronous. User messages trigger a debounced preload.trackOnly: true, the preload is sent for analytics but bids are not processed.Returns an
Ad for the given messageId. Idempotent — repeated calls with the same messageId + placement code return the same Ad. Cache the result.Live-update preload-scoped fields. See Live-updating session options.
Tear down the session: cancel preloads, destroy ads, release web views. Idempotent.
Combine publisher delivering the same events as
onEvent. Useful for SwiftUI / Combine pipelines.Read-only snapshot of messages tracked by the session.
Server-assigned session ID.
nil until the first successful preload.true if the server has permanently disabled the session via the /init response (e.g. geo-restriction). Subsequent preloads are skipped.true after destroy() is called.MutablePublisherOptions
Subset of SessionOptions accepted by session.updateOptions(_:). Every field is optional — non-nil overwrites, nil leaves unchanged.
Message
Unique message ID.
.user or .assistant.Message text.
Defaults to
Date().AdOptions
Placement code. Defaults to
"inlineAd".UI theme hint forwarded to the ad iframe (e.g.
"dark").AddMessageOptions
When
true, the preload is still sent (for analytics) but bids are not processed — no ad is generated for this message. Defaults to false.AdEvent
AdEvent is an enum with one typed payload per case. Every case has a stable string identifier accessible via event.name.
.filled(FilledData) — wire name ad.filled
A bid was filled for the placement.
.noFill(NoFillData) — wire name ad.no-fill
No ad was returned for the placement (server skipped).
.adHeight(AdHeightData) — wire name ad.height
The ad iframe reported a new height. Use to size the surrounding container (especially in UIKit UITableView / UICollectionView cells).
.viewed(ViewedData) — wire name ad.viewed
The ad was viewed by the user (IAB MRC viewability standard).
.clicked(ClickedData) — wire name ad.clicked
The user clicked the ad.
.renderStarted(RenderStartedData) — wire name ad.render-started
The first token of the ad content was received.
.renderCompleted(RenderCompletedData) — wire name ad.render-completed
Ad content streaming finished.
.error(ErrorData) — wire name ad.error
The SDK encountered an error while serving an ad.
.videoStarted(VideoStartedData) — wire name video.started
.videoCompleted(VideoCompletedData) — wire name video.completed
.rewardGranted(RewardGrantedData) — wire name reward.granted
Fired for rewarded-ad flows after the user qualifies for a reward.
Guides
Handling no-fill
Subscribe to.noFill to know when no ad was returned (geo-restriction, frequency cap, etc.). The skipCode payload tells you why.
Sizing ads in UIKit lists
InsideUITableView / UICollectionView cells, observe InlineAdUIView.onHeightChange and trigger a row resize when it fires. The height changes as the ad streams in and stabilizes at the final value.
Live-updating consent mid-session
When the user updates their consent in your CMP, call:Switching character
The activecharacter cannot be live-updated — the accumulated message history belongs to the original persona, so swapping mid-session would leave messages targeted at the wrong character.
To switch character, destroy the current session and create a new one:
publisherToken, userId, conversationId, and enabledPlacementCodes — recreate the session whenever any of those change.
Loading older messages (conversation restore)
When restoring a conversation from your backend, calladdMessage(_:) once per historical message in order. Preloads are debounced by 10ms, so rapid sequential calls coalesce into a single preload for the most recent user message — you won’t fire one preload per restored message.
Pass every message, suppress ads with trackOnly
Always feed every message into the session, even when you don’t want an ad to appear (e.g. when a user is on a free trial, in a no-ads region, or you’ve decided to show ads only every Nth message). Skipping addMessage(_:) calls breaks the conversation context the server relies on for targeting.
Use AddMessageOptions(trackOnly: true) to send the preload for analytics without generating a bid:
trackOnly: true, the preload still fires (server keeps full analytics) but no .filled event will arrive for that message and session.createAd(...) won’t resolve a bid.