ef8eab4a07
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.
150 lines
4.3 KiB
Swift
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
|