Mobile Analytics: Measuring What Users Actually Do
Implement mobile analytics that reveal real user behavior, not vanity metrics. Covers event design, user journey tracking, funnel analysis, session recording, privacy compliance, and building an analytics architecture that scales from prototype to millions of users.
Mobile analytics answers the question every product team asks but rarely answers well: what do users actually do in the app? Not what you designed them to do. Not what the user research suggested they would do. What they actually do — where they tap, where they hesitate, where they abandon, and where they convert.
Good analytics transforms product decisions from opinion-based debates into data-informed conversations.
Event Design
The Event Taxonomy
Structure events consistently across the app:
Category: The feature area
Action: What happened
Label: Additional context (optional)
Value: Numeric measurement (optional)
// Consistent event naming convention
Analytics.track("order_created", [
"order_id": "ORD-456",
"item_count": 3,
"total": 249.99,
"payment_method": "apple_pay",
"coupon_applied": true
])
Analytics.track("product_viewed", [
"product_id": "PROD-123",
"category": "electronics",
"source": "search_results",
"position": 4
])
Analytics.track("checkout_step_completed", [
"step": "shipping_address",
"step_number": 2,
"time_on_step_seconds": 45
])
Event Naming Conventions
✅ Good: snake_case, verb_past_tense
order_created, product_viewed, cart_item_added, payment_failed
❌ Bad: inconsistent case, present tense, vague
OrderCreated, view_product, click, buttonPress
What to Track
Always track:
- Screen views (every screen transition)
- Key business events (purchases, registrations, subscriptions)
- Error states (crashes, API failures, validation errors)
- User journey milestones (onboarding complete, first purchase)
Track strategically:
- Feature engagement (which features are actually used?)
- Performance events (app launch time, screen render time)
- Search queries (what are users looking for?)
- Notification interactions (open rate, dismiss rate)
Never track:
- Passwords or authentication tokens
- Personal health or financial data (without explicit consent and compliance)
- Continuous location tracking (without clear user value and consent)
User Journey Tracking
Funnel Analysis
Define conversion funnels and measure drop-off:
Checkout Funnel (last 30 days):
Cart Viewed: 10,000 (100%)
Checkout Started: 4,200 (42%) ← 58% drop-off
Shipping Entered: 3,800 (38%) ← 10% drop-off
Payment Entered: 3,400 (34%) ← 11% drop-off
Order Confirmed: 2,800 (28%) ← 18% drop-off
The biggest drop-off (58%) is between viewing the cart and starting checkout. This is where product investment should focus.
Cohort Analysis
Group users by their first action date and track retention:
Day 1 Day 7 Day 14 Day 30
Jan cohort: 100% 42% 28% 18%
Feb cohort: 100% 45% 31% 22%
Mar cohort: 100% 51% 35% 25% ← Improving!
The March cohort retains better — likely due to onboarding improvements shipped in late February.
Session Replay
Record user sessions (screen recordings with interactions) for qualitative analysis:
// Session replay integration
SessionReplay.configure(
sampleRate: 0.05, // Record 5% of sessions
maskSensitiveViews: true, // Automatically mask text inputs
maxSessionLength: 600 // 10 minutes max
)
Session replays reveal behavior that event data cannot: confusion, hesitation, repeated taps on non-interactive elements, and rage taps (rapid tapping indicating frustration).
Analytics Architecture
Client-Side Collection
┌──────────────┐
│ App UI │
│ (Events) │
└──────┬───────┘
│
┌──────▼───────┐
│ Analytics SDK │
│ (Buffer) │
└──────┬───────┘
│ batch (every 30s or 20 events)
┌──────▼───────┐
│ Analytics │
│ Endpoint │
└──────────────┘
Event Buffer and Batching
Do not send events individually. Buffer and batch:
class AnalyticsBuffer {
private val buffer = mutableListOf<AnalyticsEvent>()
private val maxBufferSize = 20
private val flushInterval = 30_000L // 30 seconds
fun track(event: AnalyticsEvent) {
buffer.add(event)
if (buffer.size >= maxBufferSize) {
flush()
}
}
private fun flush() {
if (buffer.isEmpty()) return
val batch = buffer.toList()
buffer.clear()
// Send batch to server
api.sendEvents(batch).onFailure {
// Persist to disk for retry
persistToLocalStorage(batch)
}
}
}
Offline Event Queuing
Events generated offline are queued locally and sent when connectivity returns:
func trackEvent(_ event: AnalyticsEvent) {
// Always save locally first
localStore.save(event)
// Attempt to send if online
if networkMonitor.isConnected {
uploadPendingEvents()
}
}
func uploadPendingEvents() {
let pending = localStore.getPending()
api.sendBatch(pending) { result in
if case .success = result {
localStore.markSent(pending)
}
}
}
Privacy Compliance
Consent Management
// iOS: App Tracking Transparency
import AppTrackingTransparency
func requestTracking() {
ATTrackingManager.requestTrackingAuthorization { status in
switch status {
case .authorized:
Analytics.enableFullTracking()
case .denied, .restricted:
Analytics.enableAnonymousOnly()
case .notDetermined:
break
}
}
}
Data Minimization
- Collect the minimum data needed for the analysis
- Anonymize user identifiers where possible
- Set data retention policies (delete raw events after 90 days)
- Provide data export and deletion APIs for GDPR/CCPA compliance
Server-Side Analytics
For privacy-sensitive applications, send minimal events from the client and enrich server-side:
Client sends: { event: "purchase", order_id: "456" }
Server enriches: { event: "purchase", order_id: "456",
revenue: 249.99, category: "electronics",
user_segment: "returning", cohort: "2026-01" }
User attributes and business data stay on the server. The client only reports actions.
Tool Selection
| Tool | Strengths | Best For |
|---|---|---|
| Firebase Analytics | Free, Google ecosystem, predictive | Startups, Google-heavy stacks |
| Amplitude | Best product analytics UI, cohorts | Product-led growth teams |
| Mixpanel | Event-level drill-down, funnels | Detailed behavior analysis |
| PostHog | Open source, self-hostable | Privacy-conscious organizations |
| Segment | Event router to multiple tools | Multi-tool analytics stacks |
Anti-Patterns
| Anti-Pattern | Consequence | Fix |
|---|---|---|
| Tracking everything | Data bloat, no signal in noise | Define event taxonomy, track strategically |
| No naming convention | Inconsistent data, broken funnels | Enforce naming standards in code review |
| Individual event sends | Battery drain, network overhead | Buffer and batch |
| No offline queuing | Lost events during poor connectivity | Persist events locally, sync when online |
| Ignoring privacy regulations | Legal risk, user trust erosion | ATT/consent frameworks, data minimization |
Analytics is not about data volume — it is about data quality. A thousand well-designed events that align with your product questions are worth more than a million undifferentiated event streams. Start with the questions your product team needs answered, then instrument the events that answer them.