Files
Sportstime/SportsTime/Features/Settings/ViewModels/SettingsViewModel.swift
Trey t 2917ae22b1 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>
2026-02-10 15:12:16 -06:00

94 lines
2.8 KiB
Swift

//
// SettingsViewModel.swift
// SportsTime
//
import Foundation
import SwiftUI
@MainActor
@Observable
final class SettingsViewModel {
// MARK: - User Preferences (persisted via UserDefaults)
var selectedTheme: AppTheme {
didSet {
let oldName = oldValue.displayName
ThemeManager.shared.currentTheme = selectedTheme
AnalyticsManager.shared.track(.themeChanged(from: oldName, to: selectedTheme.displayName))
}
}
var selectedSports: Set<Sport> {
didSet { savePreferences() }
}
var maxDrivingHoursPerDay: Int {
didSet {
savePreferences()
AnalyticsManager.shared.track(.drivingHoursChanged(hours: maxDrivingHoursPerDay))
}
}
// MARK: - App Info
let appVersion: String
let buildNumber: String
// MARK: - Initialization
init() {
// Load from UserDefaults using local variables first
let defaults = UserDefaults.standard
// Theme
self.selectedTheme = ThemeManager.shared.currentTheme
// Selected sports
if let sportStrings = defaults.stringArray(forKey: "selectedSports") {
self.selectedSports = Set(sportStrings.compactMap { Sport(rawValue: $0) })
} else {
self.selectedSports = Set(Sport.supported)
}
// Travel preferences
let savedDrivingHours = defaults.integer(forKey: "maxDrivingHoursPerDay")
self.maxDrivingHoursPerDay = savedDrivingHours == 0 ? 8 : savedDrivingHours
// App info
self.appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.0"
self.buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "1"
}
// MARK: - Actions
func toggleSport(_ sport: Sport) {
if selectedSports.contains(sport) {
// Don't allow removing all sports
guard selectedSports.count > 1 else { return }
selectedSports.remove(sport)
AnalyticsManager.shared.track(.sportToggled(sport: sport.rawValue, enabled: false))
} else {
selectedSports.insert(sport)
AnalyticsManager.shared.track(.sportToggled(sport: sport.rawValue, enabled: true))
}
}
func resetToDefaults() {
selectedTheme = .teal
selectedSports = Set(Sport.supported)
maxDrivingHoursPerDay = 8
AppearanceManager.shared.currentMode = .system
AnalyticsManager.shared.track(.settingsReset)
}
// MARK: - Persistence
private func savePreferences() {
let defaults = UserDefaults.standard
defaults.set(selectedSports.map(\.rawValue), forKey: "selectedSports")
defaults.set(maxDrivingHoursPerDay, forKey: "maxDrivingHoursPerDay")
}
}