SDK documentation

One Swift package, no dependencies, for iOS, watchOS, macOS, tvOS, and visionOS. The whole integration is one line; everything else is optional.

1 · Install

Xcode → File → Add Package Dependencies… → paste:

https://github.com/LudyemAS/LudyemAnalytics

2 · Configure

Create your app in the dashboard, copy its write key from the Setup tab, and call configure once, as early as possible:

import LudyemAnalytics

@main
struct MyApp: App {
    init() {
        Analytics.configure(apiKey: "lud_live_…")
    }
    var body: some Scene {
        WindowGroup {
            RootView()
                .trackAppLifecycle()   // sessions + the live "active now" heartbeat
        }
    }
}

Safe by default: Debug and Simulator builds never send anything. TestFlight events are tagged separately, so your production numbers stay clean. The write key only grants write access to your app's own stream — it can never read anything, which is why it's fine inside a shipped binary.

3 · Track anything

Analytics.track("paywall.viewed")
Analytics.track("workout.completed", metadata: ["type": "run"])

New signals appear in the dashboard's Events tab on their own — no schema, no setup. Any signal can be charted from the Overview metric picker, used as a funnel step, or wired to a push alert. The SDK also sends install exactly once per fresh install. Never put personal data in signals or metadata.

API surface

CallWhat it does
Analytics.configure(apiKey:)The whole hosted setup. Call once at launch.
Analytics.configure(_:)Full-control variant taking an AnalyticsConfiguration (below).
Analytics.track(_:metadata:)Record an event. Safe before configure (no-op). Metadata is [String: String].
.trackAppLifecycle()SwiftUI modifier for your root view: emits session.start whenever the app comes to the front after more than sessionTimeout away (a launch, or a return from the background), runs the foreground heartbeat that keeps the session alive, flushes on background.
.trackScreen(_:)SwiftUI modifier: records screen.<name> each time the view appears — the quickest way to feed the Funnel panel.
Analytics.setActive(_:)Manual foreground/background signal if you don't use the modifier (UIKit apps).
Analytics.flush()Force-send queued events now.

AnalyticsConfiguration options

OptionDefault
apiKeyrequired — your app's write key
appIDyour bundle id
flushInterval10s — how long a partial batch waits
maxBatchSize20 — flush immediately at this many queued events
heartbeatInterval60s — powers "active right now"; heartbeats are never billable
sessionTimeout300s — away longer than this and coming back is a new session (session.start); the dashboard splits sessions on the same 5-minute gap
collectsCountrytrue — the device region setting (a locale, never GPS or IP); false = empty map, same privacy labels
enabledEnvironments[.appStore, .testFlight] — Simulator and Debug never send
isEnabledtrue — master switch, e.g. behind your own user toggle

Offline & delivery guarantees

Events are written to disk as they are tracked, batched, sent in slices of 100 and retried automatically, capped at 500 events oldest-out. Every event carries a client-generated id, so a batch that is re-sent after a lost response is stored once — retries never double-count. When the app leaves the foreground the SDK flushes under a task assertion, so the send completes before iOS suspends the process.

App Store privacy labels

Declare under "Data Not Linked to You": Identifiers → User ID (the random install id) and Usage Data → Product Interaction, purpose Analytics, tracking No. No ATT prompt is required. The dashboard's Setup tab generates these answers next to your key, and the SDK ships its own PrivacyInfo.xcprivacy. Details: privacy policy.

Webhook alerts (signed JSON)

Alerts can POST signed JSON to your own server. Verify the signature: compute HMAC-SHA256 of the raw request body with the webhook's secret and compare it to the X-Ludyem-Signature header (sha256=<hex>):

# Node.js
const expected = "sha256=" + crypto.createHmac("sha256", secret)
                                   .update(rawBody).digest("hex");
const ok = crypto.timingSafeEqual(Buffer.from(expected),
                                  Buffer.from(req.headers["x-ludyem-signature"]));

Self-hosting

The SDK also runs against your own Supabase project — events go straight from devices to your database, and nothing touches Ludyem's servers:

Analytics.configure(.init(
    supabaseURL: URL(string: "https://YOUR-REF.supabase.co")!,
    publishableKey: "sb_publishable_…",
    appID: "com.you.app"
))

The schema and setup live in the repo: github.com/LudyemAS/LudyemAnalytics (supabase/schema.sql).