Quickstart

From an empty project to seeing yourself on the dashboard in about five minutes. Seven steps, each with something you can check. Already integrated and looking for a parameter? The reference is at /docs.

On this page
Shipping both? Do the steps twice — same app in the dashboard, same write key.
🤖Using Claude Code, Cursor or Copilot? Paste this prompt and skip to step 4.Shortcut

The prompt does steps 1–3 for you, then interviews you about your screens, paywall and core actions and implements the events, a funnel and — only if you want — user identification. You still need a write key: create your app in the dashboard and replace glance_live_… below, or let the agent ask you for it.

You are integrating AppGlance (privacy-first, live analytics for iOS and Android apps) into this Xcode project. Do the whole setup, then interview me about what to track and implement it. Work in small, reviewable steps and show me the diff before committing anything.

SWIFT PACKAGE: https://github.com/AppGlance/AppGlance  (module: AppGlance, version 1.0.0 or later, "Up to Next Major")
WRITE KEY (write-only, safe to ship in the binary): glance_live_…  — if this still says glance_live_…, ask me for the key; it's in the dashboard under the app's Setup tab: https://appglance.app/app/
DASHBOARD: https://appglance.app/app/

1. Add the Swift package to the app target (File → Add Package Dependencies…, or the project.yml / Package.swift if this project generates its Xcode project). Import it where needed with `import AppGlance`.

2. In the @main App's init(), as early as possible:
   AppGlance.configure(apiKey: "glance_live_…", debug: true)
   Attach `.trackAppLifecycle()` to the root view inside WindowGroup — this gives sessions, "active right now", installs and retention automatically. If the app is UIKit-based, call `AppGlance.setActive(true/false)` from the scene's foreground/background delegates instead.
   `debug: true` is there so I can verify the integration from Xcode right away: this build sends (events tagged simulator/debug — visible under the dashboard's "All" scope, never in Live) and the SDK logs each event and send to the console. Remind me to remove `debug: true` before shipping (or gate it on `#if DEBUG`). Without it, Debug and Simulator builds never send anything (TestFlight and App Store do) — that default is the right one for shipping.

3. Interview me (ask, don't guess — a few short questions, one message):
   a. What are the 3–6 key screens or steps a user moves through? (I'll get `.trackScreen("name")` on each, e.g. onboarding, paywall, the main screen.)
   b. Is there a paywall / purchase (StoreKit 2, RevenueCat…)? Where does a purchase or trial succeed?
   c. What is the core "job done" action (workout completed, entry saved, export finished…)? Any small, non-personal detail worth attaching (type, count, plan)?
   d. Do users sign in / have accounts? If so, do I want to put a name or email on their install with identify (it changes the App Store privacy answers), or keep everyone anonymous?
   e. Anything else worth counting (a feature toggled on, a share, an error the user sees)?

4. Implement the tracking plan from my answers, using ONLY these APIs:
   a. `AppGlance.track("event.name", metadata: ["key": "value"])` — event names lowercase, dot.separated, stable, ≤80 chars (e.g. paywall.viewed, purchase, trial.started, workout.completed, checkin.logged). Metadata is [String: String], short values, at most 20 keys. Never put names, emails, ids or free text a user typed into event names or metadata.
   b. `.trackScreen("paywall")` on the SwiftUI views for the key screens (records screen.paywall on appear).
   c. If there is a paywall, track `paywall.viewed`, and `purchase` / `trial.started` when they complete (with the product id as metadata) — and tell me, because that changes the App Store privacy answers (Purchases → Purchase History).
   d. Do NOT call AppGlance.identify unless I said yes in 3d — users are anonymous by default and that's the point. If I said yes: on sign-in call `AppGlance.identify(id: , email: , name: )` with only the fields I approved, and `AppGlance.reset()` on sign-out. Nothing the person didn't give the app.
   e. You may propose user properties (`AppGlance.setUserProperties(["plan": "pro"])`) for things I would want to filter users by (plan, cohort, goal…), but ask before adding them. Values are short strings; they show up as filterable pills in the dashboard's Users tab.
   f. Analytics calls must never block the UI, throw, or run in unit/UI tests. Don't add a consent banner or an ATT prompt — nothing here is tracking in Apple's sense.

5. When done, reply with:
   - the list of events you added, one line each: name — where it fires — metadata keys;
   - a suggested funnel of 2–6 of those events in order, as this link (replace <app id> with the id from my dashboard URL, and the names): https://appglance.app/app/#app/<app id>/events?funnel=paywall.viewed,purchase — opening it saves the funnel in my dashboard;
   - what my App Store privacy answers should be, based on what you implemented. Default: Identifiers → User ID and Usage Data → Product Interaction, not linked to the user, tracking No, no ATT prompt; add Purchases → Purchase History if you tracked purchases; if identify is used, add Contact Info and everything becomes "linked to the user". The dashboard's Setup tab generates the exact ticks.

Documentation: https://appglance.app/docs · this quickstart: https://appglance.app/quickstart. Keep it minimal — the whole point of AppGlance is that setup takes two minutes.You are integrating AppGlance (privacy-first, live analytics for iOS and Android apps) into this Android project. Do the whole setup, then interview me about what to track and implement it. Work in small, reviewable steps and show me the diff before committing anything.

GRADLE DEPENDENCY: implementation("app.appglance:appglance:1.0.0")  (Kotlin, minSdk 21, package app.appglance). NOTE: this artifact is not on Maven Central yet, so that line will not resolve on its own. Build it locally first: clone https://github.com/AppGlance/AppGlance, run `cd android && ./gradlew :appglance:publishToMavenLocal`, then add mavenLocal() to the project's repositories. Tell me if that fails rather than guessing at a different coordinate.
WRITE KEY (write-only, safe to ship in the binary): glance_live_…  — if this still says glance_live_…, ask me for the key; it's in the dashboard under the app's Setup tab: https://appglance.app/app/
DASHBOARD: https://appglance.app/app/

1. Add the dependency to the app module. Nothing to add to the manifest (INTERNET comes with the library). Import with `import app.appglance.AppGlance`.

2. In the Application subclass's onCreate() (create one and register it in the manifest if the app doesn't have one), as early as possible:
   AppGlance.configure(this, "glance_live_…", debug = BuildConfig.DEBUG)
   Sessions, "active right now", installs and retention are automatic (the SDK watches ProcessLifecycleOwner) — nothing to attach to activities.
   `debug = BuildConfig.DEBUG` is there so I can verify the integration from Android Studio right away: debug builds send (events tagged emulator/debug — visible under the dashboard's "All" scope, never in Live) and the SDK narrates to logcat under the tag AppGlance. Release builds behave normally: emulator and debuggable builds never send anything, production and beta builds do — that default is the right one for shipping.

3. Interview me (ask, don't guess — a few short questions, one message):
   a. What are the 3–6 key screens or steps a user moves through? (I'll get `AppGlance.trackScreen("name")` on each — Compose LaunchedEffect or onResume — e.g. onboarding, paywall, the main screen.)
   b. Is there a paywall / purchase (Play Billing, RevenueCat…)? Where does a purchase or trial succeed?
   c. What is the core "job done" action (workout completed, entry saved, export finished…)? Any small, non-personal detail worth attaching (type, count, plan)?
   d. Do users sign in / have accounts? If so, do I want to put a name or email on their install with identify (it changes the Play Data safety answers), or keep everyone anonymous?
   e. Anything else worth counting (a feature toggled on, a share, an error the user sees)?

4. Implement the tracking plan from my answers, using ONLY these APIs:
   a. `AppGlance.track("event.name", mapOf("key" to "value"))` — event names lowercase, dot.separated, stable, ≤80 chars (e.g. paywall.viewed, purchase, trial.started, workout.completed, checkin.logged). Metadata is Map<String, String>, short values, at most 20 keys. Never put names, emails, ids or free text a user typed into event names or metadata.
   b. `AppGlance.trackScreen("paywall")` where the key screens appear (records screen.paywall).
   c. If there is a paywall, track `paywall.viewed`, and `purchase` / `trial.started` when they complete (with the product id as metadata) — and tell me, because that changes the Play Data safety answers (Purchase history).
   d. Do NOT call AppGlance.identify unless I said yes in 3d — users are anonymous by default and that's the point. If I said yes: on sign-in call `AppGlance.identify(id = <account id>, email = <email>, name = <name>)` with only the fields I approved, and `AppGlance.reset()` on sign-out. Nothing the person didn't give the app.
   e. You may propose user properties (`AppGlance.setUserProperties(mapOf("plan" to "pro"))`) for things I would want to filter users by (plan, cohort, goal…), but ask before adding them. Values are short strings; they show up as filterable pills in the dashboard's Users tab.
   f. Analytics calls must never block the UI, throw, or run in unit/instrumented tests. Don't add a consent banner — nothing here uses the advertising id, IP or location.

5. When done, reply with:
   - the list of events you added, one line each: name — where it fires — metadata keys;
   - a suggested funnel of 2–6 of those events in order, as this link (replace <app id> with the id from my dashboard URL, and the names): https://appglance.app/app/#app/<app id>/events?funnel=paywall.viewed,purchase — opening it saves the funnel in my dashboard;
   - what my Google Play Data safety answers should be, based on what you implemented. Default: Device or other IDs (the random install id) and App interactions — collected, not shared, encrypted in transit, deletable on request, purpose Analytics; add Purchase history if you tracked purchases; if identify is used, add Personal info → Name / Email address.

Documentation: https://appglance.app/docs · this quickstart: https://appglance.app/quickstart?platform=android. Keep it minimal — the whole point of AppGlance is that setup takes two minutes.
Then jump to step 4 to watch it work.

1Create your app, copy the key 30 seconds

Sign up (free, no card) → New app → its name and bundle idpackage name → open the app's Setup tab → copy the write key. It looks like glance_live_….

You have a key that starts with glance_live_.
Why is it safe to put this key in my app?

It's a write key: it can only append events to this one app's stream, never read anything — which is why it's fine to ship inside your binary, and why there's nothing to keep out of a public repo besides good taste. Rotate it any time from the same tab; the old one stops working immediately.

One app in the dashboard = one thing you ship. If you have an iOS and an Android version of the same product, use one app and one key for both — the dashboard splits by OS where it matters.

2Add the package 1 minute

Xcode → File → Add Package Dependencies… → paste the URL → Dependency Rule Up to Next Major Version from 1.0.0 → Add Package → tick the AppGlance library for your app target.

https://github.com/AppGlance/AppGlance
import AppGlance compiles.
What's in the package?

One Swift package, no dependencies. iOS 16+, watchOS 9+, macOS 13+, tvOS 16+, visionOS 1+. It ships its own PrivacyInfo.xcprivacy, so Xcode's privacy report already knows what it collects. Using Package.swift or XcodeGen instead of the Xcode UI? Add .package(url: "https://github.com/AppGlance/AppGlance", from: "1.0.0") and the product AppGlance.

In your app module's build.gradle.kts:

dependencies {
    implementation("app.appglance:appglance:1.0.0")
}
Gradle syncs and import app.appglance.AppGlance resolves.

Not on Maven Central yet. The Android SDK is finished and tested against the live ingest, but the artifact is not published to a public repository, so the Gradle line above will not resolve on its own today. Build it locally in a minute instead: clone the repo, run cd android && ./gradlew :appglance:publishToMavenLocal, and add mavenLocal() to your repositories. Same code, same coordinates — when Central publishing lands, delete mavenLocal() and nothing else changes.

What's in the library?

A Kotlin library, minSdk 21, one dependency (androidx.lifecycle:lifecycle-process) — that's how sessions and "active right now" work without you touching an Activity. The INTERNET permission comes with the library's manifest; nothing else to declare. No advertising id, no location, no Play Services.

3Configure — the whole integration 1 minute

Once, as early as possible — your App's init — plus one modifier on the root view:

import AppGlance

@main
struct MyApp: App {
    init() {
        AppGlance.configure(apiKey: "glance_live_…")   // the write key from step 1
    }
    var body: some Scene {
        WindowGroup {
            RootView()
                .trackAppLifecycle()   // sessions · active right now · installs · retention
        }
    }
}
It builds. That's the whole integration — two lines.
What do these two lines do? (and UIKit)

configure is the entire hosted setup — no plist keys, no build phase, no delegate. .trackAppLifecycle() turns launches and returns from the background into sessions, keeps the quiet "still here" presence ping going while the app is open (that's what Active right now is made of), records install once, and flushes on the way out.

UIKit app? Skip the modifier and call AppGlance.setActive(true) / setActive(false) from your scene delegate's foreground / background callbacks.

Calls before configure aren't lost — they're held and replayed in order.

Once, in your Application's onCreate:

import app.appglance.AppGlance

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        AppGlance.configure(this, "glance_live_…")   // the write key from step 1
    }
}
It builds. That's the whole integration — one line.
No Application class yet? And what does the line do?

Create one and register it: <application android:name=".MyApp" …> in the manifest.

configure is the entire hosted setup. Sessions are automatic — the SDK watches the app's foreground/background state through ProcessLifecycleOwner: session.start when the app comes to the front after more than five minutes away, a quiet "still here" presence ping every minute while it's in front (that's what Active right now is made of), install once, a flush when it leaves. Nothing to attach to activities. Prefer to drive it yourself? trackAppLifecycle = false in the configuration and call AppGlance.setActive(true/false).

Calls before configure aren't lost — they're held and replayed in order.

4Run it — the checkpoint 2 minutes

⚠️ Before you hit Run: Simulator and Debug buildsemulator and debuggable builds send nothing by default.

That's deliberate — enabledEnvironments defaults to App Store + TestFlightproduction + beta, so a hundred XcodeAndroid Studio runs never touch your numbers. It also means that if you hit Run right now, the dashboard stays empty and it looks broken. So for this first run, turn on debug mode:

AppGlance.configure(apiKey: "glance_live_…", debug: true)
AppGlance.configure(this, "glance_live_…", debug = BuildConfig.DEBUG)

This build now sends — its events are tagged simulator / debug, so they show under the dashboard's All scope and never in Live — and the SDK narrates to the Xcode console. Remove debug: true before you ship (or gate it on #if DEBUG); nothing else changes. Testing on TestFlight instead? It sends by default — flip the dashboard scope to TestFlight.Debug builds now send — their events are tagged emulator / debug, so they show under the dashboard's All scope and never in Live — and the SDK narrates to logcat (tag AppGlance). Release builds are untouched: BuildConfig.DEBUG is false there, so nothing changes when you ship. Testing an internal-track build instead? Say environment = AppEnvironment.BETA in that flavor and flip the dashboard scope to Beta.

Now hit Run. In the Xcode consoleIn logcat, filtered on AppGlance, you'll see, in order:

[AppGlance] debug mode on · environment: simulator · sending to api.appglance.app · install id 9A0C…
[AppGlance] events from this build are tagged "simulator" — they show under All in the dashboard, never in Live
[AppGlance] ▸ install
[AppGlance] ▸ session.start
[AppGlance] ↑ sending 2 events…
[AppGlance] ✓ sent 2 events — on the dashboard now (scope: All)
I/AppGlance: debug mode on · environment: emulator · sending to api.appglance.app · install id 9A0C…
I/AppGlance: events from this build are tagged "emulator" — they show under All in the dashboard, never in Live
I/AppGlance: ▸ install
I/AppGlance: ▸ session.start
I/AppGlance: ↑ sending 2…
I/AppGlance: ✓ sent 2 — they're on the dashboard now (scope: All shows every environment)

Open the dashboard, set the scope (top right) to All, and watch Active right now go to 1. That's you. The app's Setup tab flips its badge to green at the same moment.

✓ Active right now = 1 · Setup badge green · console says ✓ sent.

That's the moment. Everything past here is optional.

Nothing after fifteen seconds?
  • Console says not sending: this is a Simulator runan emulator → you skipped the callout above; add debug: truedebug = BuildConfig.DEBUG.
  • Console says ✕ HTTP 401 → wrong or rotated key; copy it again from Setup.
  • Nothing from AppGlance at all → configure isn't running (is it in init()Application.onCreate() — and is the Application class registered in the manifest?), or the package isn't linked to this targetmodule.
  • Console says ✓ sent but the tile is 0 → the dashboard scope is still on Live; switch to All.
  • Emulator with no network? Events wait on disk and go out on the next try — ⟳ couldn't send … keeping 2 for the next try is the SDK doing the right thing.

5Track your first event 1 minute

Anything you name, from anywhere in the app:

AppGlance.track("paywall.viewed")
AppGlance.track("workout.completed", metadata: ["type": "run"])
AppGlance.track("paywall.viewed")
AppGlance.track("workout.completed", mapOf("type" to "run"))
The console shows ▸ paywall.viewed then ✓ sent, and the event is in the app's Events tab (scope All) within about ten seconds.
Naming rules, metadata, screens

New names appear in your app's Events tab on their own — no schema, no setup — and any of them can be charted from the Overview, used as a funnel step, or wired to a push alert. Names: lowercase, dot.separated, stable. Metadata is small string context (at most 20 keys). Never put personal data in a name or metadata — that's what identify is for (see the reference).

Screens are the cheapest funnel: .trackScreen("paywall") on a SwiftUI viewAppGlance.trackScreen("paywall") in a Compose LaunchedEffect or onResume records screen.paywall each time it appears; two or three of those and the Funnel panel has something to work with.

6Get alerts on your phone 1 minute

Dashboard → your app → Alerts → pick what deserves a push: new install, purchase, or any signal you named in step 5. Then choose where it goes:

  • ntfy or Discord — paste a topic or a webhook URL; works on any phone, and it's the quickest way to get a buzz today.
  • Your own server — a signed JSON webhook. How to verify it →
  • The AppGlance app for iPhone — native push, the live feed and your users in your pocket. In development, not on the App Store yet. What it will do →
Trigger the event once more from the app — your phone buzzes.

7Ship it and you're done

Remove debug: true (or gate it on #if DEBUG). App Store and TestFlight builds send by default; nothing else to flip.Nothing to remove — debug = BuildConfig.DEBUG is false in a release build. Production builds send by default; for Play internal/closed testing set environment = AppEnvironment.BETA in that flavor so testers land under Beta, not Live.

Your App Store privacy answersGoogle Play Data safety answers with the default setup: Identifiers → User ID and Usage Data → Product Interaction, not linked to the user, tracking No, no ATT prompt, no consent banner. The Setup tab lists the exact ticks.Device or other IDs (the random install id) and App interactions — collected, not shared, encrypted in transit, deletable on request, purpose Analytics. No advertising id, no location, no consent banner. Details, and what changes with identify →

Live scope shows real users. You'll never think about the SDK again.

Next steps

← Back to the site · Reference · See the live demo