Analytics: Privacy-Compliant Event Tracking

Whistl tracks usage analytics to improve the product while respecting user privacy. This comprehensive guide explains GA4 implementation, event taxonomy, data minimization, consent management, and how Whistl complies with GDPR, CCPA, and privacy best practices.

Why Privacy-First Analytics?

Financial apps handle sensitive data requiring extra care:

  • User trust: Privacy is essential for financial apps
  • Regulatory compliance: GDPR, CCPA require consent
  • Data minimization: Collect only what's necessary
  • Transparency: Users should know what's tracked
  • Control: Users can opt out anytime

Whistl's analytics respect privacy while providing actionable insights.

Analytics Architecture

Whistl uses Google Analytics 4 with privacy enhancements:

Data Flow

┌─────────────┐    ┌──────────────┐    ┌─────────────┐    ┌─────────────┐
│   Client    │    │   Consent    │    │   GA4       │    │   BigQuery  │
│   App       │───▶│   Manager    │───▶│   Servers   │───▶│   (Optional)│
│             │    │              │    │             │    │             │
└─────────────┘    └──────────────┘    └─────────────┘    └─────────────┘
      │                    │                    │                    │
      │  Event             │  Check             │  Process           │
      │  - Name            │  Consent           │  - Anonymize       │
      │  - Properties      │  Status            │  - Aggregate       │
      │  - User ID (hash)  │                    │  - Retain          │
      │                    │                    │                    │
      ▼                    ▼                    ▼                    ▼
  Track Event         Respect Choice      Privacy-First       Secure Storage
                      (Opt-in/out)        Analytics

Event Taxonomy

Whistl uses a structured event naming convention:

Event Categories

CategoryPrefixExamples
App Lifecycleapp_app_open, app_close, app_update
Authenticationauth_auth_login, auth_logout, auth_biometric
Navigationnav_nav_screen_view, nav_back, nav_tab_select
Interventionintervention_intervention_shown, intervention_accepted
Blockingblock_block_triggered, block_bypass_attempt
Goalsgoal_goal_created, goal_updated, goal_completed
Settingssettings_settings_changed, notifications_enabled

Event Properties

// Good: Privacy-respecting properties
{
  "event": "intervention_shown",
  "properties": {
    "intervention_type": "venue_proximity",
    "risk_level": "high",
    "step_number": 3,
    "time_of_day": "evening",
    "day_of_week": "friday"
  }
}

// Bad: Contains sensitive data (DON'T DO THIS)
{
  "event": "intervention_shown",
  "properties": {
    "user_balance": 1234.56,  // Financial data!
    "location_lat": -37.8225,  // Precise location!
    "merchant_name": "Crown Casino"  // Specific venue!
  }
}

GA4 Implementation

Google Analytics 4 configured for privacy:

iOS Implementation

import GoogleAnalytics

class AnalyticsManager {
    private var analytics: Analytics?
    private var consentGiven = false
    
    func initialize() {
        analytics = Analytics.analytics()
        
        // Privacy settings
        analytics?.setAnalyticsCollectionEnabled(false)  // Default off
        
        // Disable advertising features
        analytics?.defaultEventParameters = [
            "allow_ad_personalization_signals": false
        ]
        
        // Configure data retention (2 months minimum)
        analytics?.setDataRetentionDays(60)
    }
    
    func setConsent(_ granted: Bool) {
        consentGiven = granted
        analytics?.setAnalyticsCollectionEnabled(granted)
        
        // Update consent mode
        let consentSettings = GAConsentSettings()
        consentSettings.analyticsStorage = granted ? .granted : .denied
        consentSettings.adStorage = .denied  // Never enable ads
        GATagManager.instance.setConsent(consentSettings)
    }
    
    func trackEvent(name: String, properties: [String: Any]?) {
        guard consentGiven else { return }
        
        // Strip any sensitive data
        let sanitizedProperties = sanitize(properties)
        
        analytics?.logEvent(name, parameters: sanitizedProperties)
    }
    
    private func sanitize(_ properties: [String: Any]?) -> [String: Any]? {
        // Remove any properties that might contain PII
        let sensitiveKeys = ["email", "phone", "address", "balance", 
                            "account_number", "latitude", "longitude"]
        return properties?.filter { key, value in
            !sensitiveKeys.contains(where: key.lowercased().contains)
        }
    }
}

Android Implementation

import com.google.firebase.analytics.FirebaseAnalytics

class AnalyticsManager(private val context: Context) {
    private val analytics: FirebaseAnalytics = FirebaseAnalytics.getInstance(context)
    private var consentGiven = false
    
    fun setConsent(granted: Boolean) {
        consentGiven = granted
        
        val consent = ConsentMap.builder()
            .setConsent(ConsentType.ANALYTICS_STORAGE, 
                       if (granted) ConsentStatus.GRANTED else ConsentStatus.DENIED)
            .setConsent(ConsentType.AD_STORAGE, ConsentStatus.DENIED)  // Never ads
            .build()
        
        ConsentUpdateSdk.getInstance().updateConsent(consent)
        analytics.setAnalyticsCollectionEnabled(granted)
    }
    
    fun trackEvent(name: String, properties: Bundle?) {
        if (!consentGiven) return
        
        val sanitized = sanitize(properties)
        analytics.logEvent(name, sanitized)
    }
    
    private fun sanitize(bundle: Bundle?): Bundle? {
        // Remove sensitive parameters
        val sensitiveKeys = listOf("email", "phone", "balance", "location")
        return bundle?.let { b ->
            Bundle().apply {
                b.keySet().forEach { key ->
                    if (!sensitiveKeys.any { key.lowercase().contains(it) }) {
                        putAll(b)
                    }
                }
            }
        }
    }
}

User ID Handling

User IDs are hashed before sending to analytics:

ID Hashing

import CryptoKit

class UserIDHasher {
    private let salt = "whistl_analytics_salt_v1"
    
    func hash(userId: String) -> String {
        // Combine user ID with salt
        let data = (userId + salt).data(using: .utf8)!
        
        // SHA-256 hash
        let hash = SHA256.hash(data: data)
        
        // Return first 16 characters (64 bits)
        return hash.compactMap { String(format: "%02x", $0) }
            .joined()
            .prefix(16)
            .description
    }
}

// Result: usr-abc123 → "a1b2c3d4e5f6g7h8"
// Can't reverse engineer original ID from hash

Consent Management

Users control analytics tracking:

Consent Flow

  1. First launch: Consent dialog shown
  2. Default: Analytics disabled (opt-in)
  3. Settings: Can change anytime
  4. Withdrawal: Data deleted on opt-out

Consent Dialog

struct ConsentDialog: View {
    @State private var analyticsConsent = false
    @State private var crashReportingConsent = true  // Default on for safety
    
    var body: some View {
        VStack(spacing: 24) {
            Text("Privacy Settings")
                .font(.largeTitle)
                .fontWeight(.bold)
            
            Text("Help us improve Whistl by sharing anonymous usage data. No personal or financial information is ever shared.")
                .font(.body)
                .foregroundColor(.secondary)
                .multilineTextAlignment(.center)
            
            Toggle("Share anonymous analytics", isOn: $analyticsConsent)
            Toggle("Send crash reports", isOn: $crashReportingConsent)
            
            HStack {
                Button("Continue") {
                    saveConsent(analytics: analyticsConsent, 
                               crashes: crashReportingConsent)
                }
                .buttonStyle(.borderedProminent)
            }
        }
        .padding()
    }
}

Data Retention

Analytics data has limited retention:

Retention Settings

Data TypeRetentionReason
Event Data2 monthsMinimum GA4 allows
User Properties2 monthsTied to event data
Aggregated ReportsIndefiniteNo PII, statistical only
Crash Reports90 daysDebugging needs

Event Examples

Real events tracked by Whistl:

Key Events

// App Open
{
  "event": "app_open",
  "properties": {
    "session_id": "sess_abc123",
    "time_of_day": "morning",
    "day_of_week": "monday",
    "app_version": "1.8.2"
  }
}

// Intervention Shown
{
  "event": "intervention_shown",
  "properties": {
    "intervention_type": "spending_shield",
    "risk_level": "high",
    "step_number": 3,
    "variant": "tough_love"  // For A/B testing
  }
}

// Goal Completed
{
  "event": "goal_completed",
  "properties": {
    "goal_type": "savings",
    "days_to_complete": 45,
    "goal_id_hash": "g1a2b3c4"  // Hashed, not raw ID
  }
}

Privacy Compliance

Whistl complies with major privacy regulations:

GDPR Compliance

  • Lawful basis: Consent for analytics
  • Purpose limitation: Only for product improvement
  • Data minimization: Only necessary data collected
  • Right to access: Users can request data export
  • Right to deletion: Users can request data deletion
  • Data portability: Export in machine-readable format

CCPA Compliance

  • Right to know: Disclose what's collected
  • Right to delete: Delete on request
  • Right to opt-out: "Do Not Sell" honored
  • No discrimination: Same service regardless of choice

Analytics Dashboard

Team uses privacy-safe analytics for decisions:

Key Metrics

MetricDefinitionTarget
Daily Active UsersUnique users per dayGrowth
Session DurationAverage time in app>5 min
Intervention Acceptance% who engage with intervention>70%
Retention (D30)% active after 30 days>60%
Goal Completion Rate% goals completed>40%

Conclusion

Whistl's privacy-compliant analytics provide actionable insights while respecting user privacy. Through GA4 with enhanced privacy settings, consent management, data minimization, and limited retention, Whistl improves the product without compromising user trust.

Users control their data—analytics is opt-in, and can be disabled anytime in settings.

Privacy-First Protection

Whistl respects your privacy while continuously improving. Download free and control your analytics preferences.

Download Whistl Free

Related: GDPR & CCPA Compliance | A/B Testing Infrastructure | Local Storage Encryption