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

Swiftdata cloudkit synchronization issues
Hi, I did cloudkit synchronization using swiftdata. However, synchronization does not occur automatically, and synchronization occurs intermittently only when the device is closed and opened. For confirmation, after changing the data in Device 1 (saving), when the data is fetched from Device 2, there is no change. I've heard that there's still an issue with swiftdata sync and Apple is currently troubleshooting it, is the phenomenon I'm experiencing in the current version normal?
1
0
179
4w
ValueTransformer: Unable to determine the primitive for Attribute Error
I've been struggling to get a ValueTransformer to work while developing in Xcode 16 for iOS 18. Despite thinking I had everything set up correctly, I keep encountering the following error whenever I create a tag: let tag = Tag(name: name, color: color.toPlatformColor()) // Converts it to NSColor or UIColor modelContext.insert(tag) SwiftData/DataUtilities.swift:184: Fatal error: Unable to determine the primitive for Attribute - name: color, options: [transformable with Optional("ColorTransformer")], valueType: UIColor, defaultValue: UIExtendedSRGBColorSpace 0 0 1 1, hashModifier: nil Here’s what I’m dealing with: Tag Model: @Model public final class Tag: Identifiable { var name: String = "" @Attribute(.transformable(by: ColorTransformer.self)) var color: PlatformColor = PlatformColor.blue init(name: String, color: PlatformColor) { self.name = name self.color = color } } ColorTransformer: final class ColorTransformer: ValueTransformer { override func transformedValue(_ value: Any?) -> Any? { guard let color = value as? PlatformColor else { return nil } do { let data = try NSKeyedArchiver.archivedData( withRootObject: color, requiringSecureCoding: true) return data } catch { assertionFailure("Failed to transform `PlatformColor` to `Data`") return nil } } override func reverseTransformedValue(_ value: Any?) -> Any? { guard let data = value as? NSData else { return PlatformColor.black } do { let color = try NSKeyedUnarchiver.unarchivedObject( ofClass: PlatformColor.self, from: data as Data) return color } catch { assertionFailure("Failed to transform `Data` to `PlatformColor`") return nil } } override class func transformedValueClass() -> AnyClass { return PlatformColor.self } override class func allowsReverseTransformation() -> Bool { return true } public static func register() { ValueTransformer.setValueTransformer(ColorTransformer(), forName: .colorTransformer) } } extension NSValueTransformerName { static let colorTransformer = NSValueTransformerName(rawValue: "ColorTransformer") } Platform Alias: #if os(macOS) typealias PlatformColor = NSColor #else typealias PlatformColor = UIColor #endif The ValueTransformer is registered when the ModelContainer is created at app startup: var sharedModelContainer: ModelContainer = { ColorTransformer.register() // Other configurations... }() I've also tried not aliasing the colors to see if that changes anything, but I still encounter the same issue. Any guidance or suggestions would be greatly appreciated!
0
0
134
Oct ’24
What Is The Recommended View Hierarchy for SwiftData Many-Many Relationships?
I am building an app where tasks can be shown on multiple selected days. The user will select a day and the available tasks should be shown. I am struggling to build a view hierarchy where the visible tasks will update when added to the currently shown day. A simplified model looks like the below: @Model final class Day { private(set) var dayID: UUID = UUID() var show: [Item]? } @Model final class Item { private(set) var itemID: UUID = UUID() @Relationship(inverse: \Day.show) var showDays: [Day]? } I have a view representing a day, and I would like it to display a list of associated tasks below. The view doesn't update if I list a day's tasks directly, e.g. List(day.show) {} I get a compile error if I build a sub view that queries Item using the day's show array: let show = day.show ?? [] let predicate = #Predicate<Item> { item in show.contains(where: { $0.itemID == item.itemID }) } I get runtime error if I flip the predicate: let dayID = day.dayID let predicate: Predicate<Item> = #Predicate<Item> { item in item.showDays.flatMap { $0.contains(where: { $0.dayID == dayID }) } ?? false } I'm at a loss of how to approach this, other that just forcing the whole view hierarchy to update every time a make a change. Is there a recommended approach to this situation please?
1
0
147
Oct ’24
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).
0
0
224
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
240
4w
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
265
4w
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
0
443
2d
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
225
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
188
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
291
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 } }
5
0
272
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
3
418
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
289
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
11
0
590
1d
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
0
571
3w
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
342
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
326
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
226
Oct ’24