Docs · SDK facts

The SDK, without the sales pitch

Pure Swift Zero dependencies iOS 16+ Swift Package Manager Version-pinned releases Fire-and-forget — never blocks the main thread

The SDK's module is called FolioKit — renamed from AppCoreKit in 0.5.0. A deprecated AppCore typealias (and a legacy AppCoreKit product target) ship alongside it through the 0.x line, so apps integrated before the rename keep compiling unchanged. It's distributed via Swift Package Manager and pinned to version tags, never a moving branch.

You don't integrate it — your agent does

Adding an app in the dashboard produces one self-contained prompt. Paste it into Claude Code, Cursor, or whatever agent you build with: it adds the package, starts the SDK, wires the RevenueCat handshake, instruments your screens and key events, then calls a live verification endpoint with your app's own credentials and reports "you're live — events are flowing". The integration rules the agent follows are strict: events fire once per user action, paired events must have similar counts, and no PII goes into properties.

Automatic events

Tracked without any code:

EventWhen
app_openedlaunch and foreground (a new session starts after a background gap)
session_endapp backgrounded; session length recorded
screen_vieweach screen marked with .folioScreen("Name")
ad_attributionApple Search Ads attribution via AdServices, when present

Standard events

Instrumented by the agent at the moments that matter — the dashboard's funnel, the SKAdNetwork mapping, and the weekly brief all expect these exact names:

EventWhen
onboarding_stepeach onboarding step, with the step number
onboarding_completeduser finishes onboarding
paywall_shownthe paywall becomes visible, any trigger
trial_starteda trial begins
purchase_completeda purchase goes through

Plus a handful of app-specific events for your core value loop (scan_completed, book_finished, …) — moments a user gets value, not UI minutiae. There is deliberately no autocapture.

Delivery: offline queue, batched uploads

Events are queued on the device and uploaded in batches — roughly every 15 seconds and immediately when the app goes to the background. No network, no problem: the queue persists and retries on the next flush. Server errors are retried; permanently rejected batches are dropped rather than wedging the queue. All calls are fire-and-forget and never block the main thread.

In-app feedback

Users can send you feedback without leaving your app. Your app draws its own sheet, in its own style and at a moment it chooses, and hands the text to the SDK (FolioKit 0.6.0 and later):

let sent = await FolioKit.sendFeedback(message: text, email: email)
  • Where it shows up: the dashboard's Feedback tab, newest first, with the app version, OS, device, country and the screen it came from. Mark items read, reply by email when the user left an address, or delete them. Each new message also sends a push to the FolioKit iPhone app.
  • Delivery: saved on the device and retried like events when there is no network. sendFeedback returns false when a message can't be sent: it is empty or over 4,000 characters, the email is malformed, or the user already sent 5 that day.
  • Privacy: the text and the optional email are stored apart from analytics events and are never used for tracking. On the App Store privacy label, declare Customer Support, plus Email Address if you ask for one, as linked to the user and not used for tracking.

Remote config — change behavior without an app update

  • Feature flags and config values, fetched and cached by the SDK.
  • Server-gated rating prompt: requestReviewIfAppropriate() is a no-op until you enable it from the dashboard; it self-limits by session count and cooldown and never interrupts a first-time user.
  • Minimum version signal: set a minimum version server-side and the SDK exposes updateRequired for your own "please update" UI.
  • SKAdNetwork conversion values are mapped from the standard events by server config — retunable without shipping a build.

"No ATT prompt" — what that means, exactly

This claim gets abused in analytics marketing, so here is the precise version:

  • Apple Search Ads attribution is deterministic, through Apple's own AdServices framework. The install's campaign comes from Apple directly. No ATT consent is involved because no cross-app tracking occurs.
  • Paid social (Meta, TikTok, …) is measured in aggregate, through SKAdNetwork conversion values: Apple reports installs and conversion quality per campaign — anonymised, delayed by design, and never joinable to an individual user. You get campaign-level truth, not user-level surveillance.
  • What FolioKit will not do: per-user link-to-install-to-purchase attribution. That requires fingerprinting or deferred deep-link matching — the practices that need an ATT prompt, violate App Review rules without one, and are precisely the trap that makes MMPs look necessary.

Nothing the SDK does is "tracking" in Apple's definition (no IDFA, no fingerprinting, no cross-app device graphs), which is why no ATT prompt is required. The full list of what's collected is on the data & privacy page.

Honest limits

  • iOS only. The web snippet covers landing pages; Android when demand pulls. No Flutter or React Native wrappers today.
  • Requires RevenueCat for revenue data — FolioKit completes it, it doesn't replace it.
  • Instrumented events only: if you don't track it, it isn't there. We consider that a feature.

FolioKit — one SDK your agent installs in an afternoon, verified against a live endpoint before anyone calls it done.

Start free