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>
94 lines
2.8 KiB
Swift
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")
|
|
}
|
|
}
|