Wire this into the same code path that handles trial-to-paid renewal — typically a webhook from your billing vendor, surfaced to the client through your own backend, OR a client-side StoreKit transaction listener. Avoid passing the transaction ID, the renewal amount, or the new plan — those live in your billing system.
▸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
import StoreKit
// In your StoreKit 2 transaction observer:
Task {
for await result in Transaction.updates {
guard case .verified(let transaction) = result else { continue }
if transaction.revocationDate == nil &&
transaction.offerType == nil &&
transaction.originalID != transaction.id {
// No introductory offer + originalID != id → renewal of a previous trial = conversion.
Respectlytics.track("trial_conversion")
await transaction.finish()
}
}
}
The conversion detection is approximate from the client. The most reliable signal is server-side via App Store Server Notifications V2's DID_RENEW with offerType absent on the new transaction.
✦Privacy & implementation notes
If both your client and your backend listen to billing events, ensure exactly one of them fires the analytics event — usually the backend, because it sees the authoritative webhook even when the user has closed the app. Double-firing produces conversion rates above 100%, which always looks more dramatic than the bug actually is.
Treat trial conversion as two separate metrics with separate systems of record: the product engagement signal (Respectlytics, session-scoped) and the revenue signal (your billing system, per-user). Asking analytics to do both ends with neither being trustworthy.
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
| Trial conversion event | Firebase Analytics | Mixpanel | Respectlytics |
|---|---|---|---|
| Per-user revenue attribution | Yes | Yes | Out of scope (use billing system) |
| Transaction ID stored | Yes | Yes | Rejected by API |
| Renewal amount / currency stored | Yes | Yes | Rejected by API |
| Plan ID stored | Yes | Yes | Use distinct event_name per plan |
| Useful for product funnel? | Yes | Yes | Yes (session-grouped) |
| Useful for revenue forecasting? | Yes | Yes | No (use billing system) |
❓Frequently asked questions
Where should this event fire — client or server?
If your client gets a verifiable in-app receipt (StoreKit, Billing Library), the client is fine. If you rely on RevenueCat / Stripe webhooks for the source of truth, fire from your backend after the webhook resolves — using your server-side Respectlytics SDK or REST call. Don't fire on both — you'll double-count.
What if the user converts via a desktop web flow, not in-app?
Fire from your backend when the conversion completes — tagged with the platform field set to whatever channel produced it (platform: web). The session ID for backend-fired events is generated server-side and rotates the same way.
How do we handle different plan tiers (basic, pro, team)?
Distinct event names per tier: trial_conversion_basic, trial_conversion_pro, etc. Keep tier count modest; bucket long tails as _other.
What about double-counting if the user converts and then refunds within 24h?
Don't try to retract the analytics event. Your billing system has the refund data. The product question is "how many trials converted at all?" — that's what trial_conversion measures. Refund analysis happens against the billing system, not the analytics pipeline.