Attribura iOS SDK

Capture the answer to your in-app “How did you hear about us?” question and tie it to the content you posted — including the word-of-mouth and App Store Search traffic no tracking link can see. Headless: you build the sheet, the SDK just tracks.

GitHub repo ↗·llms.txt ↗iOS 13+ · Swift 5.9+ · zero deps

1 — Install

In Xcode: File → Add Package Dependencies… and paste https://github.com/martindochamp/attribura-ios. Or add it to your Package.swift:

swift
.package(url: "https://github.com/martindochamp/attribura-ios", from: "0.1.0")

2 — Get your token

Copy your ingest token from Settings → Integrations → Attribura SDK. It's the same per-org token the Superwall/Stripe webhooks use, and it's write-only— it can't read your data back.

3 — Configure once

swift
import Attribura

// Once, as early as possible (App init / didFinishLaunching):
Attribura.configure(
    token: "atb_ingest_xxx",
    baseURL: URL(string: "https://api.attribura.com")!
)

Signed in? Your real ingest token is already filled in above — just copy.

4 — Report the answer

swift
// When the user taps an answer in YOUR sheet.
// Use the SAME user id your revenue provider reports (Superwall app_user_id),
// so the eventual purchase inherits the self-reported channel.
Attribura.reportSource(.instagram, userId: currentUserId)

The SDK ships no UI. Here's a minimal SwiftUI sheet you own and wire up:

swift
import SwiftUI
import Attribura

struct HeardAboutUsSheet: View {
    let userId: String
    var onDone: () -> Void

    private let options: [(String, AttributionSource)] = [
        ("Instagram", .instagram), ("TikTok", .tiktok), ("YouTube", .youtube),
        ("A friend", .friend), ("A podcast", .podcast), ("App Store search", .appStoreSearch),
    ]

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("How did you hear about us?").font(.title3.bold())
            ForEach(options, id: \.0) { label, source in
                Button(label) {
                    Attribura.reportSource(source, userId: userId,
                                           prompt: "How did you hear about us?")
                    onDone()
                }
                .buttonStyle(.bordered)
            }
        }
        .padding()
    }
}

Tip — randomize the option order

People rushing onboarding tend to tap the same position every time (usually the first one), which quietly biases your data toward whatever you list first. Shuffle the options per user so that noise averages out:

swift
// Randomize the order once per user so "always tap the first option"
// habits don't skew your data — .shuffled() gives a fresh order each init.
private let options: [(String, AttributionSource)] = [
    ("Instagram", .instagram), ("TikTok", .tiktok), ("YouTube", .youtube),
    ("A friend", .friend), ("A podcast", .podcast), ("App Store search", .appStoreSearch),
].shuffled()

Sources

Linkable channels — cross-checked against your recent posts:

instagramtiktokyoutubexredditlinkedinthreadsfacebook

Dark social — unlinkable, but the traffic every link misses:

googleSearchappStoreSearchfriendpodcastnewsletterother(…)

How it resolves

The answerMethodConfidence
One recent post on that channelself_report_verified0.60 — pins the exact post
Linkable channel, 0 or several recent postsself_report0.45 — channel-level
Dark social (friend, podcast, App Store search…)dark_social0.40 — no link could see it

Deterministic signals (campaign token, discount code) always win — a survey answer never overrides a certified link. Attribution stays confidence-scored and directional, never deterministic.

Integrating with an AI assistant

Paste the SDK's llms.txtinto Claude or ChatGPT and ask it to wire the SDK into your app — it's a complete, self-contained integration guide.