Respectlytics Respectlytics

Analytics SDKs for Swift, Kotlin, Flutter and React Native

The same API in every SDK: configure once, then track events by name. No device identifiers, nothing written to the device, and MIT-licensed source on GitHub.

Free for one app and 20,000 events a month, no card needed.

Respectlytics.configure(apiKey: "your-api-key")
Respectlytics.track("purchase_completed")

Install and configure the Swift SDK

Choose your platform

Install steps, a full example and the API for each SDK.

The same three calls in every SDK

An event carries its name, and on a paid plan the properties you declared. There is no identify() and no user to reset.

configure()
Starts the SDK with your app key. Call it once, as early as the app allows: events tracked before it are dropped.
track()
Records an event by name, such as purchase_completed, with any properties you declared for it, such as an A/B test variant.
flush()
Sends queued events now. Rarely needed: the SDKs send on their own.

What leaves the app

Five fields per event and only the properties you declare, the same in every SDK. The API refuses unknown fields.

Stored for each event

event_name
the name you pass to track()
timestamp
when it happened
session_id
random, kept in memory, replaced every two hours and on each launch
platform
the operating system
country
looked up on the server from the IP address, which is then discarded

Never collected

  • the IDFA, the Advertising ID or any other device identifier
  • a fingerprint of the device
  • user IDs, names or emails
  • free-text properties on events (why)
  • anything written to the device

Filling in the App Store privacy label or the Google Play Data Safety form? The privacy disclosure guide walks through each question for the data Respectlytics handles.

What the SDKs do on their own

Nothing to configure beyond your app key.

Sessions
A random session ID in memory, replaced every two hours and on each launch.
Batches
Events are sent every 30 seconds, or as soon as 10 are waiting.
Offline
Events that cannot be sent stay in the queue and go out once the connection returns.
Memory only
The queue is never written to the device, so events not yet sent are lost if the app is force-quit.

Let an AI assistant add the events

Using Copilot, Cursor, Claude or ChatGPT? Copy this prompt into it. It asks for 10 to 20 events along your conversion funnel, placed in your own code.

Integration prompt
I want you to integrate Respectlytics analytics into my project. Respectlytics is a privacy-first analytics SDK that tracks events with minimal data collection.

## How Respectlytics counts

Respectlytics counts **sessions, not people**. A session's ID lives in memory and is replaced every two hours and on each launch, and sessions are never linked to each other. A funnel is the steps one session goes through, so choose funnel events that happen in one sitting (opening the paywall and buying), not steps days apart.

## IMPORTANT: less is more

**Target 10 to 20 events per app.** More events do not mean better analytics. Too many events create noise and make conversion path analysis meaningless, because every journey becomes unique and nothing groups. If I ask for more or fewer events, follow me.

Focus on the **conversion funnel**: what path does a session take before a purchase? If the app sells nothing, the conversion is the one action that shows the app delivered its value (a sound played to the end, a workout finished): track that instead of the purchase events.

## SDK installation

Package identifiers (copy exactly):
| Platform | Identifier |
|----------|------------|
| Swift | `https://github.com/respectlytics/respectlytics-swift.git` |
| Flutter | `respectlytics_flutter` |
| React Native | `respectlytics-react-native` |
| Kotlin | `io.github.respectlytics:respectlytics-kotlin` |

Install the SDK for my platform:

**Swift (iOS/macOS):**
```swift
// In Xcode: File > Add Package Dependencies
// URL: https://github.com/respectlytics/respectlytics-swift.git
import RespectlyticsSwift
```

**Flutter:**
```yaml
# pubspec.yaml
dependencies:
  respectlytics_flutter: ^3.1.2
```

**React Native:**
```bash
npm install respectlytics-react-native @react-native-community/netinfo
# or
yarn add respectlytics-react-native @react-native-community/netinfo
# then, for iOS
cd ios && pod install
```

**Kotlin (Android):**
```kotlin
// app/build.gradle.kts
implementation("io.github.respectlytics:respectlytics-kotlin:3.1.0")
```

## CRITICAL: initialization order

**configure() MUST be called BEFORE any track() calls.** Events tracked before configuration are dropped.

WRONG, track() before configure():
```swift
// DON'T DO THIS: events will be lost!
class HomeViewController: UIViewController {
    override func viewDidLoad() {
        Respectlytics.track("home_viewed")  // SDK not configured yet!
    }
}
// configure() called later in AppDelegate... too late!
```

CORRECT, configure() at the earliest point of the app lifecycle:
```swift
// configure() runs FIRST, before any screen can call track()
@main struct MyApp: App {
    init() {
        Respectlytics.configure(apiKey: "YOUR_API_KEY")  // First!
    }
}
```

## Configuration (MUST be first)

Call configure() once at the **earliest possible point** in the app lifecycle, before any UI renders or any track() call can run.

**Swift:** (synchronous)
```swift
// In the @main App init() or AppDelegate didFinishLaunchingWithOptions
Respectlytics.configure(apiKey: "YOUR_API_KEY")
```

**Flutter:** (async, use await!)
```dart
// In main() BEFORE runApp(), and await it!
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Respectlytics.configure(apiKey: 'YOUR_API_KEY');
  runApp(MyApp());  // Only run the app AFTER configure completes
}
```

**React Native:** (synchronous, call at module level, NOT in useEffect)
```typescript
// In index.js or App.tsx, BEFORE the component definition
import Respectlytics from 'respectlytics-react-native';

// Configure at module level: runs before any component renders
Respectlytics.configure('YOUR_API_KEY');

export default function App() {
  // track() is safe to call anywhere now
}
```

**Kotlin:** (synchronous)
```kotlin
// In Application.onCreate(), which runs before any Activity
import io.github.respectlytics.Configuration
import io.github.respectlytics.Respectlytics

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        Respectlytics.configure(Configuration(apiKey = "YOUR_API_KEY"))
    }
}
```

## Event tracking

**Swift:**
```swift
Respectlytics.track("event_name")
```

**Flutter:**
```dart
Respectlytics.track('event_name');
```

**React Native:**
```typescript
Respectlytics.track('event_name');
```

**Kotlin:**
```kotlin
Respectlytics.track("event_name")
```

## Properties and A/B tests (only when declared)

On a paid plan an event may also carry properties, but only ones the app's owner declared first in the app's Settings on respectlytics.com: a name with a list of allowed values (such as `paywall` with `a` and `b`), or a number that is kept only as its band (such as `price`). The server drops anything that was not declared, and the SDK drops free text, emails and URLs before sending. So:

- **Do not invent properties.** Add them only where I name a declared property, or where the app already shows variants of a screen (an A/B test). Otherwise use the event name alone.
- **Never** put a user ID, an email, a name or any free text in a property.
- For an A/B test, send the variant the app showed on the events of that screen, and tell me to declare the property with its values in Settings and to start a test in the dashboard's A/B tests tab.

**Swift:**
```swift
Respectlytics.track("paywall_viewed", properties: ["paywall": "b"])
```

**Flutter:**
```dart
Respectlytics.track('paywall_viewed', {'paywall': 'b'});
```

**React Native:**
```typescript
Respectlytics.track('paywall_viewed', { paywall: 'b' });
```

**Kotlin:**
```kotlin
Respectlytics.track("paywall_viewed", mapOf("paywall" to "b"))
```

## Event naming conventions

- Use snake_case: `purchase_completed`, `signup_started`
- **Lowercase letters and underscores only**: no numbers, no special characters
  - Good: `purchase_success`, `premium_plan_selected`
  - Bad: `purchase$success`, `plan-selected`, `Purchase_Completed`
- **Put the screen or context first** when the same action happens on several screens:
  - Good: `home_upgrade_tapped`, `settings_upgrade_tapped` (an upgrade button on both screens)
  - Good: `purchase_started` (it only happens in one place, so no prefix)
- **Kinds of one action share their first word, and nothing else uses it**: `subscribed_monthly` and `subscribed_annual` are counted together as `subscribed` and split by kind on the dashboard. A kind is one word after the shared one, never a step such as `started` or `failed`: a `subscribed_started` beside them keeps all of them apart.
  - Good: `subscribed_monthly`, `subscribed_annual` (one action, two kinds)
  - Bad: `purchase_monthly` beside `purchase_started` (kinds and steps share `purchase`, so nothing groups)
- Put the meaning in the event name itself; properties are only for the declared ones above

## Your task: add 10 to 20 strategic events

Analyze my codebase and add Respectlytics event tracking. **Be selective**: only track events that help explain the path to conversion. Kinds of one action (above) count as one event.

### TIER 1: ESSENTIAL (always add these 5 to 8 events)

The core conversion funnel. Every app needs these.

**Purchase funnel (pick the ones that apply):**
- `paywall_viewed`: the user sees the pricing or upgrade screen
- `purchase_started`: the user starts the purchase flow
- `purchase_success`: payment completed
- `purchase_failed`: payment failed
- `purchase_cancelled`: the user backed out

**Never leave out the completed purchase** (`purchase_success`, or the app's own conversion): every funnel ends on it. Put it where the store confirms the purchase (the transaction's result, the purchase listener or stream), not where the buy button is tapped, and only for a real success, not a pending or restored one.

**Onboarding (if the app has it):**
- `onboarding_completed`: the user finished onboarding

**Key entry points:**
- `signup_completed` or `login_completed`: the user signed in

### TIER 2: IMPORTANT (add 3 to 5 of these if relevant)

These explain behaviour before conversion:

- `[feature]_used`: main feature engagement (for example `photo_edited`, `workout_completed`)
- `[screen]_viewed`: only for key screens on the conversion path (for example `premium_features_viewed`)
- `free_trial_started`: if the app offers trials
- `share_completed`: if sharing drives growth
- `notification_opened`: if push notifications drive engagement

### TIER 3: OPTIONAL (only for a specific question)

Add these sparingly, only to answer a specific question:

- `[specific_feature]_used`: to compare converting and non-converting sessions
- `error_displayed`: if you suspect errors hurt conversion
- `help_requested`: if you suspect the UX confuses people

## DO NOT ADD (anti-patterns)

These create noise and hurt the analytics:

- A screen view for every screen (`home_viewed`, `settings_viewed`, `profile_viewed`...)
- Every button tap (`button_clicked`, `back_button_tapped`...)
- Scroll, keystroke or focus events
- Redundant purchase events (`purchase_initiated` AND `purchase_started` AND `checkout_began`)
- Tab switches, unless tabs are major features
- Every form field interaction
- Events that fire several times per session for the same action

## Important rules

1. **configure() BEFORE any track()**: critical! Put configure() at the earliest point of the app lifecycle.

2. **No free-form properties**: put the context in the event name. Pass properties to track() only when they were declared for the app in its Settings (paid plans), such as an A/B test variant; anything else is dropped.

3. **Event name format**: lowercase letters and underscores only. No characters like `$` or `-`, and no numbers.

4. **One configure() call**: call configure() once, never in individual screens.

5. **Don't track sensitive screens**: skip screens where people enter personal data (passwords, payment details).

6. **flush() is usually automatic**: the SDK sends queued events on its own. Only call it manually before logout if needed.

Now scan my project and add **10 to 20 strategic events** focused on the conversion funnel. Show me the files you would change and explain why each event matters for understanding conversion. Before you finish, count them, kinds of one action as one: fewer than 10 means a step of the funnel (onboarding, the core action, the paywall or the purchase) is still missing.

When the events are in, tell me to run the app once and open my app's setup page or dashboard on respectlytics.com, where the first event shows the moment it arrives. If you have the Respectlytics tools (its MCP server at https://respectlytics.com/mcp), call check_events with the event names you added once the app has sent them, and tell me which have not arrived.

Read what the assistant changes before you ship it. You know which events your app needs; remove the ones you will not act on.

Once the events are in, connect Respectlytics to your assistant, and it can check that they arrive.

Questions developers ask

What the SDKs do, and what they leave out on purpose.

Which platforms do the SDKs support?

Swift for iOS 15 and macOS 12 or later, Kotlin for Android 7.0 (API level 24) or later, Flutter 3.10 or later, and React Native 0.70 or later. All four share the same API: configure(), track() and flush().

Do the SDKs read the IDFA or the Advertising ID?

No. They never read the IDFA, the Android Advertising ID or any other device identifier, and they do not fingerprint the device. The only ID is a random session ID kept in memory, replaced every two hours and on each launch.

How much code does an integration take?

Two calls: configure() once when the app starts, and track() for each event you want to count. Choosing which events to track is the part worth your time.

Do the SDKs work offline?

Events wait in memory and are sent in batches, every 30 seconds or when 10 are waiting. Offline, they stay in memory until the connection returns. Nothing is written to the device, so events not yet sent are lost if the app is force-quit.

Need help with an integration?

Write to us, and we answer from the team that builds the SDKs.

Try it on your own app

Free for one app and 20,000 events a month, no card needed. Unlimited apps on every paid plan.