Swift / iOS SDK
Native ads, interstitial ads, and rewarded ads for iOS apps using SwiftUI.
New to Simula?
Head to Getting Started first to create your publisher account, get your API key, and set up your first ad unit. Then come back here to integrate the SDK.
Requirements
- iOS 15.0+
- Swift 5.9+
- Xcode 15+
Installation
Add the Simula SDK via Swift Package Manager:
- In Xcode, go to File > Add Package Dependencies
- Enter the repository URL:
https://github.com/Simula-AI-SDK/simula-ad-sdk-swift.git - Set the version rule to Up to Next Major from
1.0.1 - Add
SimulaAdSDKto your app target
Or add it directly to your Package.swift:
dependencies: [
.package(url: "https://github.com/Simula-AI-SDK/simula-ad-sdk-swift.git", from: "1.0.1"),
],
targets: [
.target(
name: "YourApp",
dependencies: [
.product(name: "SimulaAdSDK", package: "simula-ad-sdk-swift"),
]
),
]SPM and CocoaPods
The Swift SDK is distributed via Swift Package Manager and CocoaPods. Carthage is not supported.
Provider Setup (SwiftUI)
Wrap your app content with SimulaProviderView to initialize the SDK and supply configuration to all child views:
import SimulaAdSDK
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
SimulaProviderView(
apiKey: "YOUR_API_KEY",
devMode: false,
primaryUserID: "hashed_user_id"
) {
ContentView()
}
}
}
}Child views access the provider via @EnvironmentObject:
struct ContentView: View {
@EnvironmentObject var simula: SimulaProvider
var body: some View {
// Your app content
}
}Provider Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
apiKey | String | required | Your Simula API key |
devMode | Bool | false | Enables development mode — always fills, no billing, excluded from ML targeting, and enables debug console logging. Set to false before shipping |
primaryUserID | String? | nil | Hashed user ID for better ad targeting |
privacy | SimulaPrivacyConfig? | nil | Granular consent configuration. See Privacy |
telemetryEnabled | Bool | true | Enables SDK performance and error telemetry |
adContext | SimulaAdContext? | nil | Contextual targeting signals attached to every native ad request |
Imperative Initialization
For UIKit-based apps or early initialization in AppDelegate:
import SimulaAdSDK
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
SimulaAds.initialize(
apiKey: "YOUR_API_KEY",
devMode: false,
primaryUserID: "hashed_user_id"
)
return true
}
}SimulaAds.initialize() is idempotent -- the first call with a valid key wins; subsequent calls are ignored.
Updating the User ID
Update the primary user identifier at runtime after login or logout:
SimulaAds.updatePrimaryUserID("new_hashed_id") // after login
SimulaAds.updatePrimaryUserID(nil) // after logoutDev mode
Set devMode to true during development. Every ad request returns a test ad (always fills), no impressions are billed, test traffic is excluded from Simula's ML targeting models, and debug logs are printed to the console. Set to false before releasing to production.
Components
| Component | Description | Guide |
|---|---|---|
NativeAdSlot | Inline native ad card for feeds | NativeAdSlot |
SimulaInterstitialAd | Full-screen interstitial ad | InterstitialAd |
SimulaRewardedAd | Rewarded ad with play-to-earn gate | RewardedAd |
Privacy & ATT
The SDK automatically reads IAB CMP consent values from UserDefaults and ATT authorization status from the system — no setup is required for most apps. This entire section is optional. Only configure privacy manually if you need to override the auto-read values or manage consent outside a CMP.
Explicit configuration takes precedence over auto-read values.
Configuring Consent
// Full replacement
simula.updateConsent(SimulaPrivacyConfig(
tcString: "...",
gdprApplies: true,
coppaApplies: false,
enableAdvertisingId: true
))
// Partial merge
simula.updateConsent(
tcString: "...",
enableAdvertisingId: true
)
// Clear specific signals (fall back to auto-read IAB values)
simula.clearConsent(tcString: true, gppString: true)App Tracking Transparency
To collect the IDFA for better ad targeting, prompt the user with ATT:
let status = await simula.requestTrackingAuthorization()
// Returns: "authorized", "denied", "restricted", or "not_determined"You must add NSUserTrackingUsageDescription to your Info.plist:
<key>NSUserTrackingUsageDescription</key>
<string>This allows us to show you more relevant ads.</string>SimulaPrivacyConfig
| Field | Type | Default | Description |
|---|---|---|---|
tcString | String? | nil | IAB TCF v2.2 consent string |
uspString | String? | nil | IAB US Privacy (CCPA), e.g. "1YNN" |
gppString | String? | nil | IAB Global Privacy Platform string |
gppSid | String? | nil | GPP section IDs, comma-separated |
gdprApplies | Bool? | nil | Whether GDPR applies (nil = unknown) |
tcfPurpose1Consent | Bool? | nil | Explicit TCF Purpose 1 (storage) consent |
coppaApplies | Bool | false | Child-directed treatment (COPPA) |
enableAdvertisingId | Bool | false | Opt-in for IDFA collection |
Info.plist
| Key | Value | When Required |
|---|---|---|
NSUserTrackingUsageDescription | Your tracking explanation | When enableAdvertisingId: true |
SKAdNetworkItems | Array of SKAdNetwork identifiers | For privacy-preserving attribution (iOS 14+) |
The SDK bundles a PrivacyInfo.xcprivacy manifest that is automatically aggregated by Xcode. No manual privacy manifest configuration is needed.
Ad Context
Pass contextual targeting signals via adContext to improve native ad relevance. The customContext field accepts arbitrary key-values — use it to send platform-specific data like recently interacted characters.
SimulaProviderView(
apiKey: "YOUR_API_KEY",
primaryUserID: hashedUserId,
adContext: SimulaAdContext(
category: "ai-chat",
customContext: [
"recentCharacters": [
["id": "reze-01", "name": "Reze", "description": "The Bomb Devil from Chainsaw Man"],
["id": "power-02", "name": "Power", "description": "The Blood Fiend from Chainsaw Man"],
] as [[String: Any]],
"preferredGenre": "anime",
]
)
) {
ContentView()
}Update context at runtime when the user navigates to a new feed or chat:
SimulaAds.updateContext(SimulaAdContext(
category: "ai-chat",
customContext: [
"recentCharacters": [
["id": "reze-01", "name": "Reze", "description": "The Bomb Devil from Chainsaw Man"],
] as [[String: Any]],
]
))
// Clear context
SimulaAds.updateContext(nil)Error Types
All ad load and display failures are reported as SimulaAdError:
| Error | Description |
|---|---|
.notInitialized | SDK not initialized. Call SimulaAds.initialize() first |
.noSession | Session creation failed |
.noFill | No ad available for this placement |
.notReady | show() called before ad finished loading |
.stale | Loaded ad expired (1-hour limit). Call load() again |
.duplicateRequest(retryInSeconds:) | load() called while a request is already in flight |
.alreadyShowing | show() called while an ad is already on screen |
.noPresentationContext | No window scene available to present the ad |
.unsupportedPlatform | Feature not available on this platform (e.g. imperative ads on macOS) |
.network(SimulaAPIError) | Network connectivity error |
AdValue
Revenue data surfaced on DidPay delegate methods and NativeAdSlot.onPaid. All figures are serve-time estimates derived from the backend floor CPM.
public struct AdValue: Sendable, Equatable {
public let valueMicros: Int64
public let currencyCode: String
public let precisionType: PrecisionType
public let expectedCpm: Double
public let expectedRevenue: Double
}| Field | Type | Description |
|---|---|---|
valueMicros | Int64 | Per-impression revenue in micros (5000 = $0.005) |
currencyCode | String | ISO-4217 currency code, e.g. "USD" |
precisionType | PrecisionType | Estimate quality — currently always .estimated |
expectedCpm | Double | Estimated CPM (valueMicros / 1_000) |
expectedRevenue | Double | Estimated per-impression revenue (valueMicros / 1_000_000) |
