SwiftData

RSS for tag

SwiftData is an all-new framework for managing data within your apps. Models are described using regular Swift code, without the need for custom editors.

Posts under SwiftData tag

200 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

Any way to force-refresh @Query properties in SwiftUI?
I'm wondering if there is a way to force a re-fetch of a @Query property inside of a SwiftUI view so I could offer a pull-to-refresh mechanism for users to force-refresh a view. Why would I want this? iOS 18.0 and 18.1 currently contain some regressions that prevent SwiftData from properly gathering model updates caused by ModelActor's running on background threads and the suggested workarounds (listening for .NSManagedObjectContextDidSave) don't work well in most scenarios and do not cause queries in the current view to be fully re-evaluated (see Importing Data into SwiftData in the Background Using ModelActor and @Query).
1
1
663
Oct ’24
SwiftData: var dates: [Date]? or var dates: [Date] = []
I am trying to get my head around SwiftData, and specifically some more "advanced" ideas that I have not seen covered in the various tutorials. Specifically, I have a class that includes a collection that may or may not contain elements. For now I am experimenting with a simple array of Date, and I don't know if I should make it an optional, or an empty array. Without SwiftData in the mix it seems like it's probably programmers choice, but I wonder if SwiftData handles those two scenarios differently, that would suggest one over the other.
2
0
571
Oct ’24
ValueTransformer currently crashes XCode SwiftUI preview
I have a working ValueTransformer that runs fine in simulator/device, but crashes in SwiftUI Preview. Even though they are the same code. Here is my code import Foundation final class StringBoolDictTransformer: ValueTransformer { override func transformedValue(_ value: Any?) -> Any? { guard let stringBoolDict = value as? [String: Bool] else { return nil } let nsDict = NSMutableDictionary() for (key, bool) in stringBoolDict { nsDict[key] = NSNumber(value: bool) } do { let data = try NSKeyedArchiver.archivedData(withRootObject: nsDict, requiringSecureCoding: true) return data } catch { debugPrint("Unable to convert [Date: Bool] to a persistable form: \(error.localizedDescription)") return nil } } override func reverseTransformedValue(_ value: Any?) -> Any? { guard let data = value as? Data else { return nil } do { guard let nsDict = try NSKeyedUnarchiver.unarchivedDictionary(ofKeyClass: NSString.self, objectClass: NSNumber.self, from: data) else { return nil } var result = [String: Bool]() for (key, value) in nsDict { result[key as String] = value.boolValue } return result } catch { debugPrint("Unable to convert persisted Data to [Date: Bool]: \(error.localizedDescription)") return nil } } override class func allowsReverseTransformation() -> Bool { true } override class func transformedValueClass() -> AnyClass { NSDictionary.self } } and here is the container public struct SwiftDataManager { public static let shared = SwiftDataManager() public var sharedModelContainer: ModelContainer init() { ValueTransformer.setValueTransformer( StringBoolDictTransformer(), forName: NSValueTransformerName("StringBoolDictTransformer") ) let schema = Schema([, Plan.self ]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) do { sharedModelContainer = try ModelContainer(for: schema, configurations: [modelConfiguration]) } catch { fatalError("Could not create ModelContainer: \(error)") } } } and the model @Model final class Plan { @Attribute(.transformable(by: StringBoolDictTransformer.self)) var dict: [String: Bool] = [:] } I would get that container and pass it in appdelegate and it works fine. I would get that container and pass it inside a #Preview and it would crash with the following: Runtime: iOS 17.5 (21F79) - DeviceType: iPhone 15 Pro CoreFoundation: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "dict"; desired type = NSDictionary; given type = _NSInlineData; value = {length = 2, bytes = 0x7b7d}.' libsystem_c.dylib: abort() called Version 16.0 (16A242d)
2
0
878
Oct ’24
Swiftdata + Cloudkit + Mac OS how to configure for existing Swift Data store
Hi, I have a mac os app that I am developing. It is backed by a SwiftData database. I'm trying to set up cloudkit so that the app's data can be shared across the user's devices. However, I'm finding that every tutorial i find online makes it sound super easy, but only discusses it from the perspective of ios. The instructions typically say: Add the iCloud capability. Select CloudKit from its options. Press + to add a new CloudKit container, or select one of your existing ones. Add the Background Modes capability. Check the box "Remote Notifications" checkbox from its options. I'm having issue with the following: I don't see background modes showing up or remote notifications checkbox since i'm making a mac os app. If i do the first 3 steps only, when i launch my app i get an app crash while trying to load the persistent store. Here is the exact error message: Add the iCloud capability. Select CloudKit from its options. Press + to add a new CloudKit container, or select one of your existing ones. Add the Background Modes capability. Check the box "Remote Notifications" checkbox from its options. Any help would be greatly appreciated. var sharedModelContainer: ModelContainer = { let schema = Schema([One.self, Two.self]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) do { return try ModelContainer(for: schema, configurations: [modelConfiguration]) } catch { fatalError("Could not create ModelContainer: \(error)") } }() The fatal error in the catch block happens when i run the app.
7
2
1.9k
Nov ’24
How to evolve from an attribute named throws?
Many years ago I put an attribute named throws in a core data entity. Now I want to extract the data and move it to a new swift data with more function. I try to rename the entity to avoid compile problems with throws being a swift keyword, now banned as a SwiftData field. I need a code path to extract from the original core data using swift. The SwiftData macros seem to choke on the throws keyword and substitute blanks. DB rename of the attribute still uses the original throws name at the code level.
2
0
851
Oct ’24
navigationDestination with computed properties from SwiftData queries
I have a Query in my View that brings in some data that I then filter with a few different computed properties. I then use those properties to present the data in a view. This is the view (simplified for clarity). struct FMListView: View { @Query(sort: \FMList.name) var fmLists: [FMList] private var systemTodoLists: [FMList] { fmLists.filter { $0.ownership == Ownership.system } } private var userTodoLists: [FMList] { fmLists.filter { $0.ownership == Ownership.user && $0.parentList == nil} } private var favoriteTodoLists: [FMList] { fmLists.filter { $0.isFavorite } } var body: some View { NavigationStack { List { // MARK: -- System TodoLists ForEach(systemTodoLists) { list in NavigationLink(value: list) { Text(list.name) } } Section(header: Text("Favorites").padding(.top, -24)) { ForEach(favoriteTodoLists) { list in NavigationLink(value: list) { Text(list.name) } } } // MARK: -- User TodoLists Section(header: Text("Lists").padding(.top, -24)) { ForEach(fmLists.filter { $0.ownership == Ownership.user && $0.parentList == nil}) { list in NavigationLink(value: list) { Text(list.name) } } } } .navigationDestination(for: FMList.self) { list in Text(list.name) // MARK: -- ERROR HERE Toggle(isOn: list.isFavorite) { Label("Favorite", systemImage: IconConstants.starIcon) } } } } } The challenge I have here is that I need to represent the queried data in multiple different formats (for the example, I'm just using a Text view). When I navigate to the navigationDestination I want to edit the state on a property within the model but the model in this scope isn't bindable so I can't pass it into the Toggle. I'm not sure if the issue is my use of computed properties, or if it's my not understanding binding misusing the Toggle. I'd appreciate some guidance on this use-case - allowing me to pass a bindable version of the model down the stack once filtered on the app side. On a somewhat related note - I am filtering with computed properties because I can't do this filter within the Query predicate. The following gave me a compiler error because the ownership.user wasn't a constant value. $0.ownership == Ownership.user This is what the enum looks like: enum Ownership: String, CaseIterable, Codable { case system = "SYSTEM" case user = "USER" }
3
0
748
Oct ’24
Issues with SwiftData One-to-Many Relationships
I've been working with SwiftData and encountered a perplexing issue that I hope to get some insights on. When using a @Model that has a one-to-many relationship with another @Model, I noticed that if there are multiple class variables involved, SwiftData seems to struggle with correctly associating each variable with its corresponding data. For example, in my code, I have two models: Book and Page. The Book model has a property for a single contentPage and an optional array of pages. However, when I create a Book instance and leave the pages array as nil, iterating over pages unexpectedly returns the contentPage instead. You can check out the code for more details here. Has anyone else faced this issue or have any suggestions on how to resolve it? Any help would be greatly appreciated! I dont understand. How does using appended help here? I am not adding anything to the array. Here is the summary The following code defines two SwiftData models: Book and Page. In the Book class, there is a property contentPage of type Page, and an optional array pages that holds multiple Page instances. @Model class Book { var id = UUID() var title: String var contentPage: Page var pages: [Page]? init(id: UUID = UUID(), title: String, contentPage: Page) { self.id = id self.title = title self.contentPage = contentPage contentPage.book = self } func addPage(page: Page) { if pages == nil { pages = [] } page.book = self pages?.append(page) } } enum PageType: String, Codable { case contentsPage = "Contents" case picturePage = "Picture" case textPage = "Text" case blankPage = "Blank" } @Model class Page { var id = UUID() var pageType: PageType var pageNumber: Int var content: String var book: Book? init(id: UUID = UUID(), pageType: PageType, content: String, pageNumber: Int) { self.id = id self.pageType = pageType self.pageNumber = pageNumber self.content = content } } Observed Behavior: With the code above, I created a Book instance and populated all fields except for the pages, which was left as nil. However, when I attempt to iterate over the pages, I receive the contentPage instead. This indicates that there may be an issue with how SwiftData handles these associations. Expected behavior - when iterating over pages I should not see contentPage since it is a separate property
3
0
885
Oct ’24
SwiftData relationship crash on 17.x
Is this Relationship correct? Does this cause a circular reference? This runs on 18 but crashes on 17 in the swift data internals. @Model final class Item { @Attribute(.unique) var id: UUID var date: Date @Relationship(deleteRule: .nullify, inverse: \Summary.item) var summary: Summary? init(date: Date = Date.now) { self.id = UUID() self.date = Calendar.current.startOfDay(for: date) self.summary = Summary(self) } } @Model final class Summary { @Attribute(.unique) var id = UUID() @Relationship var item: Item? init(_ item: Item) { self.item = item } }
6
0
755
Oct ’24
Slow rendering List backed by SwiftData @Query
Hello, I've a question about performance when trying to render lots of items coming from SwiftData via a @Query on a SwiftUI List. Here's my setup: // Item.swift: @Model final class Item: Identifiable { var timestamp: Date var isOptionA: Bool init() { self.timestamp = Date() self.isOptionA = Bool.random() } } // Menu.swift enum Menu: String, CaseIterable, Hashable, Identifiable { var id: String { rawValue } case optionA case optionB case all var predicate: Predicate<Item> { switch self { case .optionA: return #Predicate { $0.isOptionA } case .optionB: return #Predicate { !$0.isOptionA } case .all: return #Predicate { _ in true } } } } // SlowData.swift @main struct SlowDataApp: App { var sharedModelContainer: ModelContainer = { let schema = Schema([Item.self]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) return try! ModelContainer(for: schema, configurations: [modelConfiguration]) }() var body: some Scene { WindowGroup { ContentView() } .modelContainer(sharedModelContainer) } } // ContentView.swift struct ContentView: View { @Environment(\.modelContext) private var modelContext @State var selection: Menu? = .optionA var body: some View { NavigationSplitView { List(Menu.allCases, selection: $selection) { menu in Text(menu.rawValue).tag(menu) } } detail: { DemoListView(selectedMenu: $selection) }.onAppear { // Do this just once // (0..<15_000).forEach { index in // let item = Item() // modelContext.insert(item) // } } } } // DemoListView.swift struct DemoListView: View { @Binding var selectedMenu: Menu? @Query private var items: [Item] init(selectedMenu: Binding<Menu?>) { self._selectedMenu = selectedMenu self._items = Query(filter: selectedMenu.wrappedValue?.predicate, sort: \.timestamp) } var body: some View { // Option 1: touching `items` = slow! List(items) { item in Text(item.timestamp.description) } // Option 2: Not touching `items` = fast! // List { // Text("Not accessing `items` here") // } .navigationTitle(selectedMenu?.rawValue ?? "N/A") } } When I use Option 1 on DemoListView, there's a noticeable delay on the navigation. If I use Option 2, there's none. This happens both on Debug builds and Release builds, just FYI because on Xcode 16 Debug builds seem to be slower than expected: https://indieweb.social/@curtclifton/113273571392595819 I've profiled it and the SwiftData fetches seem blazing fast, the Hang occurs when accessing the items property from the List. Is there anything I'm overlooking or it's just as fast as it can be right now?
3
4
1k
Oct ’24
modelContext.save triggers warning: publishing changes from background threads is not allowed
I am seeing a strange warning pop up in my SwiftData ModelActor: Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates. This warning is triggered by the try self.modelContext.save() call in the following function in my ModelActor: public func purgeLocalEvents(for calendarId: PersistentIdentifier) async { do { let calendars = try self.modelContext.fetch(CalendarFetchDescriptors.getCalendar(calendarId)) if let calendar = calendars.first { if let events = calendar.events { for event in events { self.modelContext.delete(event) } } calendar.lastSync = .distantPast try self.modelContext.save() } } catch { debugPrint("Error loading calendar for event purge", error.localizedDescription) } } The function in the ModelActor is called like this: Task.detached(priority: .userInitiated) { let actor = await RemoteGoogleCalendarActor(modelContainer: SwiftDataCoordinator.shared.fullContainer) await actor.purgeLocalEvents(for: calendarId) } I perform saves from modelactors in many other places, and I've never seen this warning before. What could be causing this issue?
2
0
1k
Oct ’24
SwiftData iCloud sync breaks after disabling and re-enabling iCloud
A fairly simple ModelContainer: var sharedModelContainer: ModelContainer = { let schema = Schema([ Model1.self, Model2.self, Model3.self ]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false, cloudKitDatabase: .automatic) do { let container = try ModelContainer(for: schema, migrationPlan: MigrationPlan.self, configurations: [modelConfiguration]) return container } catch { fatalError("Error: Could not create ModelContainer: \(error)") } }() After upgrading to macOS 15 and disabling/enabling iCloud for the app the sync stopped working on Mac. The steps: Go to System Settings > Apple Account > iCloud > Saved to iCloud > See all find the App and disable iCloud. After this synced items are removed from the app and some errors thrown in the console ('..unable to initialize without an iCloud account...') Re-enable the iCloud setting This error appears in the console: CoreData: error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate resetAfterError:andKeepContainer:](612): <NSCloudKitMirroringDelegate: 0x6000020dc1e0> - resetting internal state after error: Error Domain=NSCocoaErrorDomain Code=134415 "(null)" On macOS Sonoma the items are synced back to the app and the sync is restored, but on Sequoia they don't come back and the sync is not working. I tried resetting the container, deleting all data - no help. Submitted FB15455847
12
0
1.9k
Dec ’24
Error message when opening a SwiftData ModelContainer
I'm seeing these errors in the console when calling ModelContainer(for:migrationPlan:configurations) for iOS 18: error: Attempting to retrieve an NSManagedObjectModel version checksum while the model is still editable. This may result in an unstable verison checksum. Add model to NSPersistentStoreCoordinator and try again. CoreData: error: Attempting to retrieve an NSManagedObjectModel version checksum while the model is still editable. This may result in an unstable verison checksum. Add model to NSPersistentStoreCoordinator and try again. Is this anything to be concerned about? (Side note: "version" is misspelled in "verison checksum")
4
1
2.1k
Oct ’24
Does SwiftData currently supports data sharing among multiple users through iCloud?
Currently, I am planning to add a new feature to my app that allows multiple users to collaboratively manage a single legder. Initially, I chose SwiftData with iCloud for development, so I wanted to inquire whether SwiftData currently supports data sharing among multiple users through iCloud. If it does not, should I transition entirely to Core Data, or is it feasible to allow Core Data and SwiftData to work together?
1
0
1.1k
Oct ’24
how can I discern which SwiftData object trigger the .NSManagedObjectContextDidSave notification ?
Presently, I am encountering an issue with SwiftData. For instance, I have a SwiftData class Ledger that encompasses an array of SingleTransaction, which is also a SwiftData class. Here is the question: when I save a Ledger, how can I discern that the .NSManagedObjectContextDidSave notification was triggered by saving the Ledger and not by saving a SingleTransaction? This distinction is crucial to circumvent unnecessary updates. I attempted the following syntax, but Xcode indicates that Cast from NSManagedObject to unrelated type Ledger always fails. List {...} .onReceive( NotificationCenter .default .publisher(for: .NSManagedObjectContextDidSave) .receive(on: DispatchQueue.main), perform: { notification in if let userInfo = notification.userInfo, let updatedObjects = userInfo[NSUpdatedObjectsKey] as? Set<NSManagedObject> { if updatedObjects.contains(where: { $0 is Ledger }) { fetchLedgers() } } } ) What can I do?
3
0
948
Oct ’24
Swift data issue in queries in iOS 18, no pb in iOS 17
Dear community !!! I'm brand new in SwiftUI development. I created an app, I was almost at the end with fine tuning when I got weird behaviours with the iOS 18 version when using Swift Data. I think I'm part of the problem but I can not figure out how to solve it. I've searched, spent so many hours and feel a bit disappointed not succeeding. Here is my first problem : I've two models : @Model class Song: Codable { var uuid: UUID = UUID() var text: String = "" var creationDate: Date = Date.now var updatingDate: Date = Date.now var status: Int = 0 var nbRead: Int = 0 var speed: Float = 0.5 var language: String = "" enum CodingKeys: CodingKey { case uuid, text, creationDate, updatingDate, status, nbRead, speed, language } @Relationship(inverse: \Genre.songs) var genres: [Genre]? ... } and @Model class Genre { //var uuid: UUID var name: String = "" var color: String = "Red" var songs: [Song]? init( name: String, color: String) { //self.uuid = uuid self.name = name self.color = color } } I want to list all songs organised by sections : import SwiftData import SwiftUI struct SongsListView: View { private var searchingText: String @Environment(\.modelContext) private var modelContext @Query(sort: \Genre.name) private var genres: [Genre] @Query var songs : [Song] init(searchText: String) { if !searchText.isEmpty { let predicate = #Predicate<Song> { song in song.text.localizedStandardContains(searchText) } _songs = Query(filter: predicate) } searchingText = searchText } var body: some View { HStack{ List{ if !songs.isEmpty { ForEach(genres, id: \.name){ genre in Section(header: Text(genre.name)){ ForEach (songs){song in if let songGenres = song.genres { if songGenres.contains(genre){ NavigationLink { SongView(song: song) } label: { Text(song.text) } } } } } } .onDelete { indexSet in indexSet.forEach { index in let song = songs[index] modelContext.delete(song) } } Section(header: Text("Without Genre")) { ForEach (songs){song in if let songGenres = song.genres { if songGenres.isEmpty{ NavigationLink { SongView(song: song) } label: { Text(song.text) } } } } .onDelete { indexSet in indexSet.forEach { index in let song = songs[index] modelContext.delete(song) } } } } } .scrollContentBackground(.hidden) } } } On iOS 17, creating a new song adds it directly to the list in the Without Genre section. On iOS 18, it takes around 30 seconds to be added. I did a video, and I have a demo project to illustrate if necessary. Thanks a lot for any hint, advice !
1
0
546
Oct ’24
Swift Data Predicate Evaluation Crashes in Release Build When Generics Used
I'm using Swift Data for an app that requires iOS 18. All of my models conform to a protocol that guarantees they have a 'serverID' String variable. I wrote a function that would allow me to pass in a serverID String and have it fetch the model object that matched. Because I am lazy and don't like writing the same functions over and over, I used a Self reference so that all of my conforming models get this static function. Imagine my model is called "WhatsNew". Here's some code defining the protocol and the fetching function. protocol RemotelyFetchable: PersistentModel { var serverID: String { get } } extension WhatsNew: RemotelyFetchable {} extension RemotelyFetchable { static func fetchOne(withServerID identifier: String, inContext modelContext: ModelContext) -> Self? { var fetchDescriptor = FetchDescriptor<Self>() fetchDescriptor.predicate = #Predicate<Self> { $0.serverID == identifier } do { let allModels = try modelContext.fetch(fetchDescriptor) return allModels.first } catch { return nil } } } Worked great! Or so I thought... I built this and happily ran a debug build in the Simulator and on devices for months while developing the initial version but when I went to go do a release build for TestFlight, that build reliably crashed on every device with a message like this: SwiftData/DataUtilities.swift:65: Fatal error: Couldn't find \WhatsNew. on WhatsNew with fields [SwiftData.Schema.PropertyMetadata(name: "serverID", keypath: \WhatsNew., defaultValue: nil, metadata: Optional(Attribute - name: , options: [unique], valueType: Any, defaultValue: nil, hashModifier: nil)), SwiftData.Schema.PropertyMetadata(name: "title", keypath: \WhatsNew., defaultValue: nil, metadata: nil), SwiftData.Schema.PropertyMetadata(name: "bulletPoints", keypath: \WhatsNew.)>, defaultValue: nil, metadata: nil), SwiftData.Schema.PropertyMetadata(name: "dateDescription", keypath: \WhatsNew., defaultValue: nil, metadata: nil), SwiftData.Schema.PropertyMetadata(name: "readAt", keypath: \WhatsNew.)>, defaultValue: nil, metadata: nil)] It seems (cannot confirm) that something in the release build optimization process is stripping out some metadata / something about these models that makes this predicate crash. Tested on iOS 18.0 and 18.1 beta. How can I resolve this? I have two dozen types that conform to this protocol. I could manually specialize this function for every type myself but... ugh.
2
2
1.3k
Oct ’24
Thread Error with @Model Class
I have a @Model class that is comprised of a String and a custom Enum. It was working until I added raw String values for the enum cases, and afterwards this error and code displays when opening a view that uses the class: { @storageRestrictions(accesses: _$backingData, initializes: _type) init(initialValue) { _$backingData.setValue(forKey: \.type, to: initialValue) _type = _SwiftDataNoType() } get { _$observationRegistrar.access(self, keyPath: \.type) return self.getValue(forKey: \.type) } set { _$observationRegistrar.withMutation(of: self, keyPath: \.type) { self.setValue(forKey: \.type, to: newValue) } } } Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1cc165d0c) I removed the String raw values but the error persists. Any guidance would be greatly appreciated. Below is replicated code: @Model class CopingSkillEntry { var stringText: String var case: CaseType init(stringText: String, case: CaseType) { self.stringText = stringText self.case = case } } enum CaseType: Codable, Hashable { case case1 case case1 case case3 }
2
0
431
Oct ’24
Should I worry about this error message?
I have a SwiftData Model which includes a property of type Array. @Model final class Handler { public var id: UUID public var name: String public var mailingAddress: [String] . . . public init() { self.id = UUID() self.name = "" self.mailingAddress = [] . . . } When I use this Model in code, the mailingAddress field appears to work just as I would expect, but when saving it (running in Simulator), I see the following logged message: CoreData: fault: Could not materialize Objective-C class named "Array" from declared attribute value type "Array<String>" of attribute named mailingAddress Should I be worried about this?
0
1
452
Oct ’24