feat: add PostHog analytics with full event tracking across app

Integrate self-hosted PostHog (SPM) with AnalyticsManager singleton wrapping
all SDK calls. Adds ~40 type-safe events covering trip planning, schedule,
progress, IAP, settings, polls, export, and share flows. Includes session
replay, autocapture, network telemetry, privacy opt-out toggle in Settings,
and super properties (app version, device, pro status, selected sports).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-02-10 15:12:16 -06:00
parent 5389fe3759
commit 2917ae22b1
20 changed files with 989 additions and 23 deletions

View File

@@ -39,6 +39,9 @@ struct SettingsView: View {
// Travel Preferences
travelSection
// Privacy
privacySection
// Icon Generator
iconGeneratorSection
@@ -79,6 +82,7 @@ struct SettingsView: View {
Button {
withAnimation(.easeInOut(duration: 0.2)) {
AppearanceManager.shared.currentMode = mode
AnalyticsManager.shared.track(.appearanceChanged(mode: mode.displayName))
}
} label: {
HStack(spacing: 12) {
@@ -181,7 +185,10 @@ struct SettingsView: View {
Section {
Toggle(isOn: Binding(
get: { DesignStyleManager.shared.animationsEnabled },
set: { DesignStyleManager.shared.animationsEnabled = $0 }
set: {
DesignStyleManager.shared.animationsEnabled = $0
AnalyticsManager.shared.track(.animationsToggled(enabled: $0))
}
)) {
Label {
VStack(alignment: .leading, spacing: 2) {
@@ -257,6 +264,42 @@ struct SettingsView: View {
.listRowBackground(Theme.cardBackground(colorScheme))
}
// MARK: - Privacy Section
private var privacySection: some View {
Section {
Toggle(isOn: Binding(
get: { !AnalyticsManager.shared.isOptedOut },
set: { enabled in
if enabled {
AnalyticsManager.shared.optIn()
} else {
AnalyticsManager.shared.optOut()
}
}
)) {
Label {
VStack(alignment: .leading, spacing: 2) {
Text("Share Analytics")
.font(.body)
.foregroundStyle(.primary)
Text("Help improve SportsTime by sharing anonymous usage data")
.font(.caption)
.foregroundStyle(.secondary)
}
} icon: {
Image(systemName: "chart.bar.xaxis")
.foregroundStyle(Theme.warmOrange)
}
}
} header: {
Text("Privacy")
} footer: {
Text("No personal data is collected. Analytics are fully anonymous.")
}
.listRowBackground(Theme.cardBackground(colorScheme))
}
// MARK: - About Section
private var aboutSection: some View {
@@ -573,7 +616,7 @@ struct SettingsView: View {
Button {
Task {
await StoreManager.shared.restorePurchases()
await StoreManager.shared.restorePurchases(source: "settings")
}
} label: {
Label("Restore Purchases", systemImage: "arrow.clockwise")
@@ -584,7 +627,7 @@ struct SettingsView: View {
}
.listRowBackground(Theme.cardBackground(colorScheme))
.sheet(isPresented: $showPaywall) {
PaywallView()
PaywallView(source: "settings")
}
}