From 7da7995538252d3707248b8fd65646e9a75d46f2 Mon Sep 17 00:00:00 2001 From: alexandrev-tibco Date: Thu, 30 Apr 2026 11:24:56 +0200 Subject: [PATCH] Fix SwiftData migration crash on schema upgrades 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 --- MealMood/MealMoodApp.swift | 15 ++++++++++++++- MealMood/Models/AppSettings.swift | 8 ++++---- MealMood/Models/Dish.swift | 2 +- MealMood/Models/MealSlot.swift | 4 ++-- MealMood/Models/Tag.swift | 6 +++--- 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/MealMood/MealMoodApp.swift b/MealMood/MealMoodApp.swift index 3ef2a30..47343cb 100644 --- a/MealMood/MealMoodApp.swift +++ b/MealMood/MealMoodApp.swift @@ -17,7 +17,20 @@ struct MealMoodApp: App { MealSlot.self ]) let configuration = ModelConfiguration(cloudKitDatabase: .none) - return try! ModelContainer(for: schema, configurations: [configuration]) + do { + return try ModelContainer(for: schema, configurations: [configuration]) + } catch { + // Migration failed — reset the store to avoid a crash loop + let storeURL = configuration.url + for ext in ["", "-shm", "-wal"] { + try? FileManager.default.removeItem(at: URL(fileURLWithPath: storeURL.path + ext)) + } + do { + return try ModelContainer(for: schema, configurations: [configuration]) + } catch { + fatalError("Cannot create ModelContainer after reset: \(error)") + } + } }() init() { diff --git a/MealMood/Models/AppSettings.swift b/MealMood/Models/AppSettings.swift index 040e072..59c2acf 100644 --- a/MealMood/Models/AppSettings.swift +++ b/MealMood/Models/AppSettings.swift @@ -4,11 +4,11 @@ import SwiftData @Model final class AppSettings { var mealWindows: String // "dinnerOnly", "lunchOnly", "both" - var includeWeekends: Bool + var includeWeekends: Bool = true var language: String // "spanish", "english" var calendarId: String? - var syncEnabled: Bool + var syncEnabled: Bool = false var syncMode: String? // "weekComplete", "manual" var lunchTime: Date var dinnerTime: Date @@ -18,8 +18,8 @@ final class AppSettings { var iCloudSyncEnabled: Bool? var weekExportStyle: String? - var isPremium: Bool - var onboardingCompleted: Bool + var isPremium: Bool = false + var onboardingCompleted: Bool = false init() { self.mealWindows = "dinnerOnly" diff --git a/MealMood/Models/Dish.swift b/MealMood/Models/Dish.swift index 6ffc44e..f7b136b 100644 --- a/MealMood/Models/Dish.swift +++ b/MealMood/Models/Dish.swift @@ -8,7 +8,7 @@ final class Dish { var descriptionText: String? var tagIds: [UUID] var createdAt: Date - var isPriority: Bool + var isPriority: Bool = false init( id: UUID = UUID(), diff --git a/MealMood/Models/MealSlot.swift b/MealMood/Models/MealSlot.swift index daa21b9..2813c83 100644 --- a/MealMood/Models/MealSlot.swift +++ b/MealMood/Models/MealSlot.swift @@ -8,8 +8,8 @@ final class MealSlot { var mealType: String // "lunch", "dinner" var dishId: UUID? var calendarEventId: String? - var isRuleOverridden: Bool - var isEatingOut: Bool + var isRuleOverridden: Bool = false + var isEatingOut: Bool = false var weekPlan: WeekPlan? diff --git a/MealMood/Models/Tag.swift b/MealMood/Models/Tag.swift index 5e6ca2d..e93cde4 100644 --- a/MealMood/Models/Tag.swift +++ b/MealMood/Models/Tag.swift @@ -8,11 +8,11 @@ final class Tag { var nameEN: String var color: String var maxPerWeek: Int? - var noConsecutive: Bool - var noDuplicateInDay: Bool + var noConsecutive: Bool = false + var noDuplicateInDay: Bool = false var mealTypeRestriction: String? // "lunch", "dinner", nil var dayRestriction: String? // nil = any day, "weekdays" = Mon-Fri, "weekend" = Sat-Sun - var isDefault: Bool + var isDefault: Bool = true var sortOrder: Int init(