Owlmetry
SDKsSwift SDK

Configuration

Configure the Swift SDK with Owl.configure() — API key, endpoint, and options.

Call Owl.configure() once, as early as possible in your app's lifecycle. This initializes the SDK, starts a new session, and begins capturing events.

Basic Setup

In a SwiftUI app, call configure() in the @main App init():

import SwiftUI
import Owlmetry

@main
struct MyApp: App {
    init() {
        do {
            try Owl.configure(
                endpoint: "https://ingest.owlmetry.com",
                apiKey: "owl_client_..."
            )
        } catch {
            print("Owlmetry configuration failed: \(error)")
        }
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

For UIKit apps, call it in application(_:didFinishLaunchingWithOptions:):

import Owlmetry

func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    do {
        try Owl.configure(
            endpoint: "https://ingest.owlmetry.com",
            apiKey: "owl_client_..."
        )
    } catch {
        print("Owlmetry configuration failed: \(error)")
    }
    return true
}

Parameters

ParameterTypeDefaultDescription
endpointStringrequiredYour Owlmetry ingest server URL.
apiKeyStringrequiredClient API key. Must start with owl_client_.
flushOnBackgroundBooltrueAutomatically flush buffered events when the app enters the background.
compressionEnabledBooltrueGzip-compress request bodies before sending.
networkTrackingEnabledBooltrueAutomatically track URLSession HTTP requests.
consoleLoggingBooltruePrint events to the Xcode console / terminal. Each line is prefixed with 🦉 Owlmetry and the log level. Set to false to suppress.
attributionEnabledBooltrueAuto-capture Apple Search Ads attribution on configure. See below.

Full Example with All Options

try Owl.configure(
    endpoint: "https://ingest.owlmetry.com",
    apiKey: "owl_client_...",
    flushOnBackground: true,
    compressionEnabled: true,
    networkTrackingEnabled: false,  // disable if you don't want HTTP request tracking
    consoleLogging: true           // print events to console (default: true)
)

What Happens on Configure

  1. Session ID -- a fresh UUID is generated to group all events from this launch.
  2. Bundle ID -- automatically read from Bundle.main.bundleIdentifier.
  3. Debug mode -- is_dev is set to true in DEBUG builds (#if DEBUG), false in release builds.
  4. Launch time -- the SDK measures the time from process start to the configure() call and includes it as _launch_ms on the session_started event. Place configure() as early as possible for an accurate cold-start measurement.
  5. Session event -- a sdk:session_started event is emitted immediately with the _launch_ms attribute.
  6. Lifecycle observer -- if flushOnBackground is true, the SDK registers for app lifecycle notifications to flush on background and persist on termination.
  7. Network monitor -- starts tracking connection type (wifi, cellular, ethernet, offline) via NWPathMonitor.

Configuration Errors

Owl.configure() is a throwing function. The OwlConfigurationError enum covers three cases:

ErrorWhen it occurs
invalidEndpointThe endpoint string is not a valid URL.
invalidApiKeyThe API key does not start with owl_client_.
missingBundleIdBundle.main.bundleIdentifier is nil or empty. This can happen in command-line tools or certain test environments.

Apple Search Ads Attribution

When attributionEnabled is true (the default), the SDK auto-captures Apple Search Ads attribution shortly after Owl.configure(). It asks the AdServices framework for an attribution token, POSTs it to /v1/identity/attribution/apple-search-ads, and the server resolves it against Apple's Attribution API and writes the results to the current user's properties.

The SDK writes one of:

  • attribution_source = "apple_search_ads" plus asa_campaign_id, asa_ad_group_id, asa_keyword_id, asa_claim_type, asa_ad_id, asa_creative_set_id when Apple attributes the install.
  • attribution_source = "none" when Apple responds but the install is not attributed, or after ASA_MAX_PENDING_ATTEMPTS (5) failed retries.
  • attribution_source = "apple_test_install" (with no asa_* fields) when Apple's response carries its deliberate non-production fixture — same numeric ID across campaign, ad group, and ad. This fires for TestFlight builds, Xcode-deployed dev builds on real devices, and the iOS simulator. Filter these out of acquisition dashboards alongside organic installs by excluding attribution_source IN ('none', 'apple_test_install') so a developer's own install doesn't get counted as a paid acquisition. The placeholder IDs are intentionally dropped so they don't pollute reports or trigger an Apple Ads enrichment call.

The SDK's live path only populates numeric IDs — Apple's AdServices Attribution API doesn't return human-readable names. Name fields (asa_campaign_name, asa_ad_group_name, asa_ad_name, asa_keyword) are populated by one of two server-side integrations, both of which cover every attributed user and are per-field merged so the live SDK's numeric IDs are never overwritten:

  • Apple Search Ads integration (recommended, self-contained) — Owlmetry calls Apple's Campaign Management API directly to resolve IDs → names.
  • RevenueCat integration with RC's Advanced Apple AdServices integration configured — RC resolves the names server-side on AdServices token receipt; Owlmetry pulls them via RC sync and webhooks.

Capture runs once per anonymous ID and is idempotent — the SDK records completion in UserDefaults and skips on subsequent launches. If Apple's attribution record isn't ready yet (HTTP 404), the server returns pending: true and the SDK retries on the next launch, up to 5 attempts across ~24h.

To opt out entirely:

try Owl.configure(
    endpoint: "https://ingest.owlmetry.com",
    apiKey: "owl_client_...",
    attributionEnabled: false
)

If you disable auto-capture, you can still submit a token manually — e.g. after showing an ATT prompt — with:

let sent = await Owl.sendAppleSearchAdsAttributionToken(token)

Owl.resetAppleSearchAdsAttributionCapture() clears the captured-once flag if you ever need to force a retry.

Attribution properties carry through the identity claim: when an anonymous user is upgraded via Owl.setUser(), asa_* and attribution_source merge into the real user's properties.

Debugging attribution from the dashboard

Every capture attempt emits an sdk:attribution_capture event so you can spot failures without attaching a debugger. The _outcome attribute distinguishes the states:

_outcomeLevelExtra attributesWhen
successinfo_attribution_sourceApple returned a decisive answer (apple_search_ads, none, or apple_test_install).
pendinginfo_attempt, _max_attemptsApple 404 — record not ready yet. Will retry on next launch.
gave_upwarn_attemptsHit the 5-pending cap; SDK wrote attribution_source = "none".
token_fetch_failedwarn_errorAAAttribution.attributionToken() threw on-device.
invalid_tokenwarnOwlmetry rejected the token as empty or malformed; never retried. Common in regions where AdServices is unavailable or the user disabled personalized ads.
transport_failureerrorOwlmetry POST failed after transport retries.

Filter the dashboard events list by sdk:attribution_capture to see the capture outcome for each install.

Calling Configure More Than Once

Calling configure() again is safe. The SDK will shut down the previous transport, stop the old lifecycle observer, and start fresh with a new session ID. This generates a new sdk:session_started event.

Shutdown

In most cases, the SDK handles cleanup automatically via background task flushing and termination persistence. If you need to guarantee delivery at a specific point, call shutdown() explicitly:

await Owl.shutdown()

Next Steps

Ready to get started?

Connect your agent via MCP or CLI and start tracking.