Fire on level-completion success — after the win-state animation begins, before any post-level dialog. Encode the level into the event name (level_complete_1, level_complete_2). Don't pass score, attempts, or duration.
▸Install the Swift (iOS) SDK
// Package.swift
dependencies: [
.package(url: "https://github.com/respectlytics/respectlytics-swift.git", from: "3.0.0")
]
// Or via Xcode → File → Add Packages → paste the URL above.
The SDK ships only via Swift Package Manager. CocoaPods and Carthage are not published — fewer integration paths means fewer surfaces to keep audited.
▸Initialize Respectlytics in Swift (iOS)
import Respectlytics
@main
struct MyApp: App {
init() {
Respectlytics.configure(appKey: "<YOUR_APP_KEY>")
}
var body: some Scene { WindowGroup { ContentView() } }
}
Call configure once at app launch — typically in your App struct's init. No Info.plist keys are required: the SDK does not call ATTrackingManager and does not request the IDFA, so NSUserTrackingUsageDescription should NOT be added.
▸Track the event in Swift (iOS)
import Respectlytics
func handleLevelComplete(levelNumber: Int) {
// Encode level in event name; bucket by 50 if you have >50 levels.
if levelNumber <= 50 {
Respectlytics.track("level_complete_\(levelNumber)")
} else {
let bucket = ((levelNumber - 1) / 50) * 50 + 1
let upper = bucket + 49
Respectlytics.track("level_complete_\(bucket)_\(upper)")
}
}
For high-level-count games, the bucketing strategy keeps your event-name taxonomy navigable — under 50 buckets total even for thousand-level games.
✦Privacy & implementation notes
Per-player progression — score curves, leaderboard positions, skill ratings, items inventory — lives in your game backend. That's where it has authority and where the gameplay logic reads from. Mirroring it into product analytics duplicates a system of record without adding signal you can't get from the game backend's own reporting.
Most game-product decisions are about rate ("is this level a difficulty wall for new players?") rather than skill ("how quickly does this player progress?"). The rate question is session-grouped. The skill question is per-player and lives in your game backend.
Apple rejected approximately 3% of apps in 2024 for incorrectly omitting NSUserTrackingUsageDescription when ATT was required by the SDKs they shipped. Respectlytics doesn't trigger ATT. The corollary is also true: do not add the key on Respectlytics's account — its presence implies you track across apps, even if your code never calls requestTrackingAuthorization.
Internally the Swift SDK uses Swift Concurrency: events are queued in an actor-isolated buffer (RAM-only), flushed on a 30-second timer and on UIApplication.willResignActiveNotification. Force-quit before flush drops queued events — by design. There is no UserDefaults or file backing.
⇋How this compares to other analytics SDKs
| Level complete event | Firebase Analytics | Mixpanel | GameAnalytics | Respectlytics |
|---|---|---|---|---|
| Level number as parameter | Yes | Yes | Yes | Use distinct event_name |
| Score stored | Recommended | Recommended | Yes | Out of scope (game backend) |
| Attempts to complete | Recommended | Recommended | Yes | Out of scope (game backend) |
| Time-to-complete | Recommended | Recommended | Yes | Server-side derivation |
| Per-player progression curve | Yes | Yes | Yes | Out of scope (game backend) |
❓Frequently asked questions
Won't we run out of distinct event names if we have 1000 levels?
For high-level-count games, bucket: level_complete_1_50, level_complete_51_100, etc. The Respectlytics aggregation handles either shape. Per-level granular progression is a game-backend signal — your leaderboards and skill-rating system already have it.
How do we measure difficulty without per-player attempts?
Per-session. The rate of sessions where level_complete_N follows level_complete_N-1 is your level-N completion rate. Country-bucketed and device-platform-bucketed shows where the difficulty cliff sits.
What about retries?
Distinct event name: level_failed_N. The rate of level_failed_N to level_complete_N over a session is your level-N retry signal.
Should we instrument every level individually?
If you have under 50 levels, yes — distinct event names per level. Past that, bucket. The principle is to keep your event-name taxonomy navigable; 1000 distinct level event names is functionally a parameter.