ESC
Type to search guides, tutorials, and reference documentation.
Verified by Garnet Grid

Mobile App Analytics Engineering

Instrument mobile applications for actionable analytics. Covers event taxonomy design, user journeys, retention metrics, funnel analysis, privacy-preserving analytics, and the patterns that transform raw events into product insights.

Mobile analytics is different from web analytics. Users install, use, and uninstall. Sessions span days, not minutes. Offline usage is common. Privacy regulations restrict tracking. Good mobile analytics tracks what matters — user journeys, retention, and value — without drowning in vanity metrics.


Event Taxonomy

# Design a consistent event naming convention
# Format: {category}_{action}_{target}

# User lifecycle events
events = {
    # Onboarding
    "app_opened_first_time": {"source": "install"},
    "onboarding_started": {},
    "onboarding_step_completed": {"step": "int", "step_name": "string"},
    "onboarding_completed": {"duration_seconds": "int"},
    "onboarding_skipped": {"at_step": "int"},
    
    # Core actions
    "search_performed": {"query": "string", "results_count": "int"},
    "item_viewed": {"item_id": "string", "category": "string"},
    "item_added_to_cart": {"item_id": "string", "price": "float"},
    "checkout_started": {"cart_value": "float", "item_count": "int"},
    "purchase_completed": {"order_id": "string", "total": "float", "payment_method": "string"},
    
    # Engagement
    "feature_used": {"feature_name": "string", "context": "string"},
    "notification_received": {"type": "string"},
    "notification_opened": {"type": "string", "delay_seconds": "int"},
    "share_initiated": {"content_type": "string", "platform": "string"},
    
    # Technical
    "error_occurred": {"error_code": "string", "screen": "string"},
    "performance_metric": {"screen": "string", "load_time_ms": "int"},
    "crash_detected": {"exception": "string", "stack_trace": "string"},
}

Key Metrics

Acquisition:
  Install rate: Downloads / App Store impressions
  Cost per install: Ad spend / Installs
  Organic vs paid ratio: Organic installs / Total installs
  
Activation:
  Onboarding completion rate: Completed / Started
  Time to first key action: Median time from install to first [purchase/post/etc.]
  Day 1 retention: % of users who return day after install
  
Retention:
  Day 1/7/30 retention: % returning on day N
  Rolling 7-day retention: Active in last 7 days / Active 14-7 days ago
  Cohort retention curves: Retention by install week
  
  Healthy benchmarks:
    Day 1: 25-40%
    Day 7: 10-20%
    Day 30: 5-10%
    (Top apps: 2-3x these numbers)
    
Revenue:
  ARPU: Revenue / Active Users
  ARPPU: Revenue / Paying Users
  Conversion rate: Paying users / Active users
  LTV: Lifetime value per user (predicted revenue over lifetime)
  
Engagement:
  DAU/MAU ratio: Daily active / Monthly active (stickiness)
  Sessions per day: Average sessions per active user
  Session duration: Median session length
  Feature adoption: % of users using feature X

Implementation

// iOS Analytics SDK wrapper (Swift)
class AnalyticsManager {
    static let shared = AnalyticsManager()
    
    private var providers: [AnalyticsProvider] = []
    private var userProperties: [String: Any] = [:]
    
    func configure() {
        providers = [
            FirebaseAnalyticsProvider(),
            MixpanelProvider(),
        ]
    }
    
    func identify(userId: String, properties: [String: Any]) {
        userProperties = properties
        providers.forEach { $0.identify(userId: userId, properties: properties) }
    }
    
    func track(_ event: String, properties: [String: Any] = [:]) {
        // Enrich with standard properties
        var enriched = properties
        enriched["platform"] = "ios"
        enriched["app_version"] = Bundle.main.appVersion
        enriched["os_version"] = UIDevice.current.systemVersion
        enriched["timestamp"] = ISO8601DateFormatter().string(from: Date())
        enriched["session_id"] = SessionManager.shared.currentSessionId
        
        // Send to all providers
        providers.forEach { $0.track(event, properties: enriched) }
        
        // Local logging for debugging
        #if DEBUG
        print("📊 Analytics: \(event) \(enriched)")
        #endif
    }
    
    func trackScreen(_ screenName: String) {
        track("screen_viewed", properties: [
            "screen_name": screenName,
            "previous_screen": NavigationTracker.shared.previousScreen ?? "none",
        ])
    }
}

Anti-Patterns

Anti-PatternConsequenceFix
Track everythingData noise, storage costs, analysis paralysisTrack decisions, not clicks
No event naming conventionInconsistent data, impossible to queryStandardized taxonomy
Client-side only analyticsLost events, ad blockers, offline gapsServer-side event validation
Vanity metrics (total downloads)Misleading growth narrativeFocus on retention and activation
No cohort analysisCannot detect if product is improvingWeekly cohort retention curves

Mobile analytics is not about collecting data — it is about collecting the right data, structuring it consistently, and using it to make better product decisions.

Jakub Dimitri Rezayev
Jakub Dimitri Rezayev
Founder & Chief Architect • Garnet Grid Consulting

Jakub holds an M.S. in Customer Intelligence & Analytics and a B.S. in Finance & Computer Science from Pace University. With deep expertise spanning D365 F&O, Azure, Power BI, and AI/ML systems, he architects enterprise solutions that bridge legacy systems and modern technology — and has led multi-million dollar ERP implementations for Fortune 500 supply chains.

View Full Profile →