7da7995538
Add = false/true defaults to all non-optional Bool properties so SwiftData's lightweight migration knows what value to assign existing rows. Replace try! ModelContainer with crash-safe do-catch that resets the store on migration failure instead of crashing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
181 lines
5.1 KiB
Swift
181 lines
5.1 KiB
Swift
import Foundation
|
|
import SwiftData
|
|
|
|
@Model
|
|
final class AppSettings {
|
|
var mealWindows: String // "dinnerOnly", "lunchOnly", "both"
|
|
var includeWeekends: Bool = true
|
|
var language: String // "spanish", "english"
|
|
|
|
var calendarId: String?
|
|
var syncEnabled: Bool = false
|
|
var syncMode: String? // "weekComplete", "manual"
|
|
var lunchTime: Date
|
|
var dinnerTime: Date
|
|
var eventDuration: Int
|
|
var eventPrefix: String
|
|
var reminderMinutesBefore: Int?
|
|
var iCloudSyncEnabled: Bool?
|
|
var weekExportStyle: String?
|
|
|
|
var isPremium: Bool = false
|
|
var onboardingCompleted: Bool = false
|
|
|
|
init() {
|
|
self.mealWindows = "dinnerOnly"
|
|
self.includeWeekends = true
|
|
self.language = "system"
|
|
self.calendarId = nil
|
|
self.syncEnabled = false
|
|
self.syncMode = CalendarSyncMode.weekComplete.rawValue
|
|
// Default lunch time 14:00
|
|
var lunchComponents = DateComponents()
|
|
lunchComponents.hour = 14
|
|
lunchComponents.minute = 0
|
|
self.lunchTime = Calendar.current.date(from: lunchComponents) ?? Date()
|
|
// Default dinner time 21:00
|
|
var dinnerComponents = DateComponents()
|
|
dinnerComponents.hour = 21
|
|
dinnerComponents.minute = 0
|
|
self.dinnerTime = Calendar.current.date(from: dinnerComponents) ?? Date()
|
|
self.eventDuration = 60
|
|
self.eventPrefix = "🍽️"
|
|
self.reminderMinutesBefore = nil
|
|
self.iCloudSyncEnabled = true
|
|
self.weekExportStyle = nil
|
|
self.isPremium = false
|
|
self.onboardingCompleted = false
|
|
}
|
|
|
|
var mealWindowsEnum: MealWindows {
|
|
get { MealWindows(rawValue: mealWindows) ?? .dinnerOnly }
|
|
set { mealWindows = newValue.rawValue }
|
|
}
|
|
|
|
var languageEnum: AppLanguage {
|
|
get { AppLanguage(rawValue: language) ?? .system }
|
|
set { language = newValue.rawValue }
|
|
}
|
|
|
|
var syncModeEnum: CalendarSyncMode {
|
|
get { CalendarSyncMode(rawValue: syncMode ?? "") ?? .weekComplete }
|
|
set { syncMode = newValue.rawValue }
|
|
}
|
|
|
|
var iCloudSyncEnabledResolved: Bool {
|
|
get { iCloudSyncEnabled ?? true }
|
|
set { iCloudSyncEnabled = newValue }
|
|
}
|
|
|
|
var weekExportStyleEnum: WeekExportStyle {
|
|
get { WeekExportStyle(rawValue: weekExportStyle ?? "") ?? .defaultStyle }
|
|
set { weekExportStyle = newValue.rawValue }
|
|
}
|
|
}
|
|
|
|
enum MealWindows: String, CaseIterable {
|
|
case dinnerOnly
|
|
case lunchOnly
|
|
case both
|
|
|
|
var localizedKey: String {
|
|
switch self {
|
|
case .dinnerOnly: return "meal_windows_dinner_only"
|
|
case .lunchOnly: return "meal_windows_lunch_only"
|
|
case .both: return "meal_windows_both"
|
|
}
|
|
}
|
|
|
|
var icon: String {
|
|
switch self {
|
|
case .dinnerOnly: return "moon.stars.fill"
|
|
case .lunchOnly: return "sun.max.fill"
|
|
case .both: return "sun.and.horizon.fill"
|
|
}
|
|
}
|
|
}
|
|
|
|
enum AppLanguage: String, CaseIterable {
|
|
case system
|
|
case spanish
|
|
case english
|
|
case french
|
|
case german
|
|
case italian
|
|
case portugueseBrazil
|
|
|
|
var displayName: String {
|
|
switch self {
|
|
case .system: return "System"
|
|
case .spanish: return "Español"
|
|
case .english: return "English"
|
|
case .french: return "Français"
|
|
case .german: return "Deutsch"
|
|
case .italian: return "Italiano"
|
|
case .portugueseBrazil: return "Português (Brasil)"
|
|
}
|
|
}
|
|
|
|
var localeIdentifier: String {
|
|
switch self {
|
|
case .system: return resolved().localeIdentifier
|
|
case .spanish: return "es"
|
|
case .english: return "en"
|
|
case .french: return "fr"
|
|
case .german: return "de"
|
|
case .italian: return "it"
|
|
case .portugueseBrazil: return "pt-BR"
|
|
}
|
|
}
|
|
|
|
func resolved() -> AppLanguage {
|
|
if self != .system { return self }
|
|
let localeIdentifier = Locale.current.identifier.lowercased()
|
|
|
|
if localeIdentifier.hasPrefix("es") { return .spanish }
|
|
if localeIdentifier.hasPrefix("fr") { return .french }
|
|
if localeIdentifier.hasPrefix("de") { return .german }
|
|
if localeIdentifier.hasPrefix("it") { return .italian }
|
|
if localeIdentifier.hasPrefix("pt") { return .portugueseBrazil }
|
|
return .english
|
|
}
|
|
}
|
|
|
|
enum MealType: String, Codable, CaseIterable {
|
|
case lunch
|
|
case dinner
|
|
|
|
var icon: String {
|
|
switch self {
|
|
case .lunch: return "sun.max.fill"
|
|
case .dinner: return "moon.stars.fill"
|
|
}
|
|
}
|
|
}
|
|
|
|
enum CalendarSyncMode: String, CaseIterable {
|
|
case weekComplete
|
|
case manual
|
|
|
|
var localizedKey: String {
|
|
switch self {
|
|
case .weekComplete: return "settings_sync_mode_week_complete"
|
|
case .manual: return "settings_sync_mode_manual"
|
|
}
|
|
}
|
|
}
|
|
|
|
enum WeekExportStyle: String, CaseIterable {
|
|
case defaultStyle
|
|
case schoolTimetable
|
|
case vertical
|
|
|
|
var localizedKey: String {
|
|
switch self {
|
|
case .defaultStyle: return "share_export_style_default"
|
|
case .schoolTimetable: return "share_export_style_school"
|
|
case .vertical: return "share_export_style_vertical"
|
|
}
|
|
}
|
|
}
|