Post

Replies

Boosts

Views

Activity

SwiftData Bug with .modelContext in iOS 18
I'm using SwiftData to persist my items in storage. I used .modelContext to pass in my shared context, and on iOS 18 (both on a physical device and a simulator), I discovered a bug where SwiftData doesn't automatically save my data. For example, I could add a new item, go to the next screen, change something that reloads a previous screen, and SwiftData just forgets the item that I added. Please find the fully working code attached. While writing this post, I realized that if I use .modelContainer instead of .modelContext, the issue is solved. So I have two questions: It seems like .modelContainer is the go-to option when working with SwiftData, but why did an issue occur when I used .modelContext and passed in a shared container? When should we use .modelContext over .modelContainer? What was the bug? It's working fine in iOS 17, but not in iOS 18. Or is this expected? Here's the fully working code so you can copy and paste: import SwiftUI import SwiftData typealias NamedColor = (color: Color, name: String) extension Color { init(r: Double, g: Double, b: Double) { self.init(red: r/255, green: g/255, blue: b/255) } static let namedColors: [NamedColor] = [ (.blue, "Blue"), (.red, "Red"), (.green, "Green"), (.orange, "Orange"), (.yellow, "Yellow"), (.pink, "Pink"), (.purple, "Purple"), (.teal, "Teal"), (.indigo, "Indigo"), (.brown, "Brown"), (.cyan, "Cyan"), (.gray, "Gray") ] static func name(for color: Color) -> String { return namedColors.first(where: { $0.color == color })?.name ?? "Blue" } static func color(for name: String) -> Color { return namedColors.first(where: { $0.name == name })?.color ?? .blue } } @main struct SwiftDataTestApp: App { var sharedModelContainer: ModelContainer = { let schema = Schema([ Item.self, ]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) do { return try ModelContainer(for: schema, configurations: [modelConfiguration]) } catch { fatalError("Could not create ModelContainer: \(error)") } }() @AppStorage("accentColor") private var accentColorName: String = "Blue" var body: some Scene { WindowGroup { NavigationStack { HomeView() } .tint(Color.color(for: accentColorName)) } .modelContainer(sharedModelContainer) // This works // .modelContext(ModelContext(sharedModelContainer)) // This doesn't work } } @Model final class Item { var timestamp: Date init(timestamp: Date) { self.timestamp = timestamp } } struct HomeView: View { @State private var showSettings = false @Environment(\.modelContext) var modelContext @AppStorage("accentColor") private var accentColorName: String = "Blue" @Query private var items: [Item] var body: some View { List { ForEach(items) { item in NavigationLink { Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))") } label: { Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard)) } } Button { withAnimation { let newItem = Item(timestamp: Date()) modelContext.insert(newItem) } } label: { Image(systemName: "plus") .frame(maxWidth: .infinity) .frame(maxHeight: .infinity) } } .navigationTitle("Habits") .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button(action: { showSettings = true }) { Label("", systemImage: "gearshape.fill") } } } .navigationDestination(isPresented: $showSettings) { colorPickerView } } private var colorPickerView: some View { Form { Section(header: Text("Accent Color")) { Picker("Accent Color", selection: $accentColorName) { ForEach(Color.namedColors, id: \.name) { namedColor in Text(namedColor.name) .tag(namedColor.name) .foregroundColor(namedColor.color) } } .pickerStyle(.wheel) } } .navigationTitle("Settings") } }
1
4
660
Aug ’24