Files
honeyDueKMP/iosApp/iosApp/Helpers/ThemeManager.swift
T
Trey t ef8eab4a07 iOS: complete bundle ID + team ID migration to com.myhoneydue.*
Carries the rebrand from the backend (APPLE_CLIENT_ID, APNS_TOPIC) all
the way through the iOS targets:

- All target PRODUCT_BUNDLE_IDENTIFIERs: com.tt.honeyDue.* → com.myhoneydue.honeyDue.*
- DEVELOPMENT_TEAM: V3PF3M6B6U → X86BR9WTLD (across every target)
- APP_GROUP_IDENTIFIER: group.com.tt.honeyDue.* → group.com.myhoneydue.honeyDue.*
- BGTaskSchedulerPermittedIdentifiers + BackgroundTaskManager constant
- KeychainHelper service identifier
- StoreKit fallback product IDs + Info.plist IAP product ID keys
- ExportOptions.plist teamID
- NSCamera / NSPhotoLibrary usage descriptions reworded
- Onboarding suggestion strings reworked (new %lld%% match copy,
  dropped old "Great match" / "Good match" / "Generating suggestions"
  strings — replaced by relevance-percentage labels)
- xctestplan + settings.local.json housekeeping

App-group rename means UserDefaults / shared-container data written to
the old group ID is abandoned. Acceptable since this is pre-launch.
2026-05-01 18:30:52 -07:00

150 lines
4.3 KiB
Swift

import SwiftUI
#if !WIDGET_EXTENSION
import Combine
#endif
// MARK: - Theme ID Enum
enum ThemeID: String, CaseIterable, Codable {
case bright = "Default"
case teal = "Teal"
case ocean = "Ocean"
case forest = "Forest"
case sunset = "Sunset"
case monochrome = "Monochrome"
case lavender = "Lavender"
case crimson = "Crimson"
case midnight = "Midnight"
case desert = "Desert"
case mint = "Mint"
var displayName: String {
rawValue
}
var description: String {
switch self {
case .bright:
return "Vibrant iOS system colors"
case .teal:
return "Blue-green with warm accents"
case .ocean:
return "Deep blues and coral tones"
case .forest:
return "Earth greens and golden hues"
case .sunset:
return "Warm oranges and reds"
case .monochrome:
return "Elegant grayscale"
case .lavender:
return "Soft purple with pink accents"
case .crimson:
return "Bold red with warm highlights"
case .midnight:
return "Deep navy with sky blue"
case .desert:
return "Warm terracotta and sand tones"
case .mint:
return "Fresh green with turquoise"
}
}
// Preview colors for theme selection
var previewColors: [Color] {
let theme = self.rawValue
return [
Color("\(theme)/Primary", bundle: nil),
Color("\(theme)/Secondary", bundle: nil),
Color("\(theme)/Accent", bundle: nil)
]
}
}
// MARK: - Shared App Group UserDefaults
private let appGroupID: String = {
Bundle.main.infoDictionary?["AppGroupIdentifier"] as? String ?? "group.com.myhoneydue.honeyDue.dev"
}()
private let sharedDefaults: UserDefaults = {
guard let defaults = UserDefaults(suiteName: appGroupID) else {
#if DEBUG
assertionFailure("App Group '\(appGroupID)' not configured — theme won't sync to widgets")
#endif
return UserDefaults.standard
}
return defaults
}()
// MARK: - Theme Manager
#if WIDGET_EXTENSION
// Simplified ThemeManager for widget extensions (no ObservableObject needed)
class ThemeManager {
static let shared = ThemeManager()
var currentTheme: ThemeID {
// Load saved theme from shared App Group defaults
if let savedThemeRawValue = sharedDefaults.string(forKey: themeKey),
let savedTheme = ThemeID(rawValue: savedThemeRawValue) {
return savedTheme
}
return .bright
}
var honeycombEnabled: Bool {
sharedDefaults.bool(forKey: honeycombKey)
}
private let themeKey = "selectedTheme"
private let honeycombKey = "honeycombEnabled"
private init() {}
}
#else
// Full ThemeManager for main app with ObservableObject support
@MainActor
class ThemeManager: ObservableObject {
static let shared = ThemeManager()
@Published var currentTheme: ThemeID {
didSet {
saveTheme()
}
}
@Published var honeycombEnabled: Bool {
didSet {
sharedDefaults.set(honeycombEnabled, forKey: honeycombKey)
}
}
private let themeKey = "selectedTheme"
private let honeycombKey = "honeycombEnabled"
private init() {
// Load saved theme from shared App Group defaults
if let savedThemeRawValue = sharedDefaults.string(forKey: themeKey),
let savedTheme = ThemeID(rawValue: savedThemeRawValue) {
self.currentTheme = savedTheme
} else {
self.currentTheme = .bright
}
self.honeycombEnabled = sharedDefaults.bool(forKey: honeycombKey)
}
private func saveTheme() {
// Save to shared App Group defaults so widgets can access it
sharedDefaults.set(currentTheme.rawValue, forKey: themeKey)
// Update reactive source so all views using Color.appPrimary etc. re-render
AppThemeSource.shared.themeName = currentTheme.rawValue
}
func setTheme(_ theme: ThemeID) {
currentTheme = theme
}
func setHoneycomb(_ enabled: Bool) {
honeycombEnabled = enabled
// Update reactive source so honeycomb overlays re-render
AppThemeSource.shared.honeycombEnabled = enabled
}
}
#endif