No IPs. No ad IDs. No idea who your users are.
SwiftStats measures how your app is used without collecting anything that identifies the person using it. Privacy isn't a setting you turn on — the SDK has no code path that gathers it.
- Retained
- Event names and the flat properties you send, timestamps, a session id and a hashed install id. Per batch: app version and build, SDK version, OS and version, device model, architecture, locale, region setting, debug flag; screen size, light or dark mode and TestFlight status only if your app supplies them. A hashed user id only if you call
identify(). - Never retained
- IP addresses, IDFA, email, name, location, free text, anything linking one app to another.
- Install id
- A random UUID salted and hashed on device, scoped to one app. It counts installs; it does not describe a person.
- No ATT prompt
NSPrivacyTrackingis false and there are no tracking domains. A test in the repo asserts it.
Opens rose 8.2% while active installs rose 3.1%. Existing installs are opening the app more often rather than new ones arriving.
It tells you what changed
Every read-out is a deterministic rule over your own numbers. No model, no guessing — the rule that produced each sentence is shown next to it.
It admits what it doesn't know
When a figure is an upper bound, when consent limited it, when a batch never arrived — the dashboard says so on the tile, not in a support article.
Yours if you want it
The SDK and the Cloudflare backend are MIT. Run the whole pipeline in your own account, or let us host it and skip the ops.
One package, one key, first event in minutes
Add the Swift package, paste the write key the setup wizard hands you, and watch the page flip to Connected when your first batch lands. Zero dependencies, Swift 6 language mode, actor-based.
- iOS 18+, iPadOS 18+, macOS 15+
- Apple system frameworks only — no third-party dependencies to audit
track()returns once the event is on disk, so a kill doesn't lose it- Write keys are append-only and scoped to one project
import Stats
import StatsCloudflare
let stats = StatsClient(configuration: StatsConfiguration(
appId: "com.example.MyApp",
installIdSalt: "a-constant-per-app-string",
sink: CloudflareSink(
endpoint: try CloudflareEndpoint(string: "https://api.swiftstats.co"),
writeKey: writeKey
),
consent: .all,
autoEvents: [.appOpen, .appBackground, .sessions]
))
await stats.track("paywall_shown", props: ["variant": "b"])Start free, or run it yourself
Unlimited events on every tier. Self-hosting always free. One app on one platform, free forever. No card required.