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 Relationship Persistence
I'm still getting started with SwiftData and having trouble understanding how to persist a model with a relationship in a container. I've tried to distill the example below to be pretty simple. Here are some example model definitions. I would like to be able to define a n Item. That Item can have SubItems related to it in a one-to-many fashion. Each SubItem is required to be attached to a parent Item. final class Item { var name: String var subitems: [Item]? init( name: String, subitems: [SubItem]? = nil ) { self.name = name } } @Model final class SubItem { var name: String init(name: String) { self.name = name } } In my app I am then defining a preview container with some pre-saved data already. Item( name: "item1", subitems: [ SubItem(name: "subItemA"), SubItem(name: "subItemB") ] ), Item( name: "item2", subitems: [ SubItem(name: "subItemC"), SubItem(name: "subItemD") ] ) ] @MainActor let PreviewContainer: ModelContainer = { do { let schema = Schema([ Item.self, SubItem.self, ]) let container = try ModelContainer( for: schema, configurations: ModelConfiguration(isStoredInMemoryOnly: true) ) for item in PreviewItems { container.mainContext.insert(item) for subItem in item.subitems! { container.mainContext.insert(subItem) } } return container } catch { fatalError("Failed to create container") } }() My app is then defined as follows... struct SwiftDataTestApp: App { var body: some Scene { WindowGroup { ContentView() } .modelContainer(PreviewContainer) } } struct ContentView: View { @Environment(\.modelContext) private var modelContext @Query private var items: [Item] var body: some View { HStack { VStack { Text(items[0].name) Text(String(items[0].subitems!.count)) // Text(items[0].subitems![0].name) // Text(items[0].subitems![1].name) } Spacer() VStack { Text(items[1].name) Text(String(items[1].subitems!.count)) // Text(items[0].subitems![0].name) // Text(items[0].subitems![1].name) } } .padding(100) } } #Preview { ContentView() .modelContainer(PreviewContainer) } The preview loads without an issue, but if I uncomment the lines that access the SubItems it crashes. In the preview I can also see that each Item has 0 SubItems related to it. For some reason, the model container is not actually storing the `SubItem even though they are defined in PreviewItems. Some things I've tried Explicitly adding the relationship Adding a Item property in SubItem to link back to it's parent In the PreviewContainer definition, manually insert the SubItems as well as the parent Items Any help is appreciated, thanks
1
0
259
Sep ’24
One to Many Relationship in SwiftData
Hi, I understand how to make one to many relationship in SwiftData and how to show the child records, like all cities of a country. But how to navigate and show the parent record from a child record, Like I want to show a country of a city ? like country.cities show all cities of a country, will cities.country work to show the country ? like cities.country.name ? Kind Regards
3
0
247
Sep ’24
Swiftdata Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1cc1698ec)
Hello, i have a route with many points for routes: @Model public class Route(){ public id: UUID = UUID() var name: String var desc: String var points: [Point] = [] } @Model public class Point(){ public id: UUID = UUID() var speed: double var route : Route } when I like to add point. route.point.append(point) I get all ways this error: Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1cc1698ec) my Xcode version 15.3
0
0
308
Sep ’24
Getting a strange SwiftData error when generating samples using AVFoundation
Hi all, I'm getting a strange SwiftData error at runtime in my voice recorder app. Whenever I attempt to generate and cache a samples array so that my app can visualize the waveform of the audio, the app crashes and the following pops up in Xcode: { @storageRestrictions(accesses: _$backingData, initializes: _samples) init(initialValue) { _$backingData.setValue(forKey: \.samples, to: initialValue) _samples = _SwiftDataNoType() } get { _$observationRegistrar.access(self, keyPath: \.samples) return self.getValue(forKey: \.samples) } set { _$observationRegistrar.withMutation(of: self, keyPath: \.samples) { self.setValue(forKey: \.samples, to: newValue) } } } With an execution breakpoint on the line _$observationRegistrar.withMutation(of: self, keyPath: \.samples). Here is my model class: import Foundation import SwiftData @Model final class Recording { var id : UUID? var name : String? var date : Date? var samples : [Float]? = nil init(name: String) { self.id = UUID() self.name = name self.date = Date.now } } And here is where the samples are being generated (sorry for the long code): private func processSamples(from audioFile: AVAudioFile) async throws -> [Float] { let sampleCount = 128 let frameCount = Int(audioFile.length) let samplesPerSegment = frameCount / sampleCount let buffer = try createAudioBuffer(for: audioFile, frameCapacity: AVAudioFrameCount(samplesPerSegment)) let channelCount = Int(buffer.format.channelCount) let audioData = try readAudioData(from: audioFile, into: buffer, sampleCount: sampleCount, samplesPerSegment: samplesPerSegment, channelCount: channelCount) let processedResults = try await processAudioSegments(audioData: audioData, sampleCount: sampleCount, samplesPerSegment: samplesPerSegment, channelCount: channelCount) var samples = createSamplesArray(from: processedResults, sampleCount: sampleCount) samples = applyNoiseFloor(to: samples, noiseFloor: 0.01) samples = normalizeSamples(samples) return samples } private func createAudioBuffer(for audioFile: AVAudioFile, frameCapacity: AVAudioFrameCount) throws -> AVAudioPCMBuffer { guard let buffer = AVAudioPCMBuffer(pcmFormat: audioFile.processingFormat, frameCapacity: frameCapacity) else { throw Errors.AudioProcessingError } return buffer } private func readAudioData(from audioFile: AVAudioFile, into buffer: AVAudioPCMBuffer, sampleCount: Int, samplesPerSegment: Int, channelCount: Int) throws -> [[Float]] { var audioData = [[Float]](repeating: [Float](repeating: 0, count: samplesPerSegment * channelCount), count: sampleCount) for segment in 0..<sampleCount { let segmentStart = AVAudioFramePosition(segment * samplesPerSegment) audioFile.framePosition = segmentStart try audioFile.read(into: buffer) if let channelData = buffer.floatChannelData { let dataCount = samplesPerSegment * channelCount audioData[segment] = Array(UnsafeBufferPointer(start: channelData[0], count: dataCount)) } } return audioData } private func processAudioSegments(audioData: [[Float]], sampleCount: Int, samplesPerSegment: Int, channelCount: Int) async throws -> [(Int, Float)] { try await withThrowingTaskGroup(of: (Int, Float).self) { taskGroup in for segment in 0..<sampleCount { let segmentData = audioData[segment] taskGroup.addTask { var rms: Float = 0 vDSP_rmsqv(segmentData, 1, &rms, vDSP_Length(samplesPerSegment * channelCount)) return (segment, rms) } } var results = [(Int, Float)]() for try await result in taskGroup { results.append(result) } return results } } private func createSamplesArray(from processedResults: [(Int, Float)], sampleCount: Int) -> [Float] { var samples = [Float](repeating: 0, count: sampleCount) vDSP_vfill([0], &samples, 1, vDSP_Length(sampleCount)) for (segment, rms) in processedResults { samples[segment] = rms } return samples } private func applyNoiseFloor(to samples: [Float], noiseFloor: Float) -> [Float] { var result = samples let noiseFloorArray = [Float](repeating: noiseFloor, count: samples.count) vDSP_vsub(noiseFloorArray, 1, samples, 1, &result, 1, vDSP_Length(samples.count)) return result } private func normalizeSamples(_ samples: [Float]) -> [Float] { var result = samples var min: Float = 0 var max: Float = 0 vDSP_minv(samples, 1, &min, vDSP_Length(samples.count)) vDSP_maxv(samples, 1, &max, vDSP_Length(samples.count)) if max > min { var a: Float = 1.0 / (max - min) var b: Float = -min / (max - min) vDSP_vsmsa(samples, 1, &a, &b, &result, 1, vDSP_Length(samples.count)) } else { vDSP_vfill([0.5], &result, 1, vDSP_Length(samples.count)) } return result } And this is how the processSamples function is used: private func loadAudioSamples() async { let url = recording.fileURL if let audioFile = loadAudioFile(url: url) { if recording.samples == nil { recording.samples = try? await processSamples(from: audioFile) } } } private func loadAudioFile(url: URL) -> AVAudioFile? { do { let audioFile = try AVAudioFile(forReading: url) return audioFile } catch { return nil } } Any help or leads would be greatly appreciated! Thanks!
1
0
314
Sep ’24
Modifying SwiftData model causes deadlock
I've had a persistent but hard to reliably reproduce issue when using SwiftData with SwiftUI. Sometimes, when some properties of a SwiftData model object are used in the UI (like the title of a note in a list of notes) and something in the model is modified, the UI stops responding. The modification in the model could be a completely different object and of a different type and could be as simple as toggling a Bool, it could be performed on the main thread from inside a Button's action using the ModelContext from the environment or on a separate thread using a ModelActor. The issue even appears and disappears between builds of the same code. As an example, this is the kind of code that leads to this issue, though I haven't been able to make a minimal reproduction because of how inconsistently it appears: @ModelActor struct NoteModifier { func append() { guard let notes = try? modelContext.fetch(FetchDescriptor<Note>()) else { return } notes.randomElement()!.title.append("ABC") try? modelContext.save() } } struct ContentView: View { @Environment(\.modelContext) private var modelContext @Query private var notes: [Note] var body: some View { VStack { List { ForEach(notes) { note in VStack { Text(note.title) Text(note.contents) } } } Button { Task { await NoteModifier(modelContainer: modelContext.container).append() } } label: { Text("Bug") } } } } When it happens and I try pausing execution, it's locked inside kevent_id in the getter for one of the properties of my model object like this: In Instruments:
0
0
187
Sep ’24
There is some sort of bug with iCloud container. What should I do?
I am getting the following error: Failed to save diary entry to CloudKit: <CKError 0x6000035adec0: "Server Rejected Request" (15/2000); op = 10F3CACEA9EC09B6; uuid = 22DDB0B8-9F1B-4BE6-A51B-2ADD08B469B1; container ID = "iCloud.com.domainname.productname"> What should I do to prevent this from happening? I put the right container name and still this happens. I was expecting the data model object to save in the iCloud.
0
0
277
Sep ’24
Swift Data Predicate failing for String Array
In Swift Data I have a basic model @Model class MovieSD { @Attribute(.unique) var title: String var genre: [String] = [String]() init(title: String) { self.title = title } } I am trying to create predicates when fetching the data. Creating a predicate to search by title works as expected let movieTitle = #Predicate<MovieSD> { movie in movie.title.contains("filterstring") } But when attempting to do the same for the String array let movieGenre = #Predicate<MovieSD> { movie in movie.genre.contains("filterstring") } Results in a crash with EXC_BAD_ACCESS Similar approaches produce a different error and point to the likely issue. let movieGenre2 = #Predicate<MovieSD> { movie in if movie.genre.contains(where: { $0 == "filterstring" }) { return true } else { return false } } Results in a crash with the error: error: SQLCore dispatchRequest: exception handling request: <NSSQLFetchRequestContext: 0x281a96840> , Can't have a non-relationship collection element in a subquerySUBQUERY(genre, $$_local_1, $$_local_1 == "SciFi") with userInfo of (null) or alternatively largely the same error for: let movieGenre3 = #Predicate<MovieSD> { movie in movie.genre.filter { genre in return genre == "filterstring" }.count > 0 } But I couldn't seem to find an approach to create a SUBQUERY with #Predicate Naturally, I can use similar to the above to filter the array that is returned. But this seems inefficient. And it seems it should be possible to filter Any help showing how to filter a String array with a predicate with Swift Data would be appreciated
0
0
268
Sep ’24
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
691
2w
.onMove drag and drop
Hi everyone, I’m working on a SwiftUI project where I want the entire page to be scrollable. Within this page, I have a bottom section that displays a list of items, and these items are being rendered from a SwiftData model object. My main goal is to make this list reorderable using the onMove method. The challenge I’m facing is that using a List inside a ScrollView requires a fixed height for the List, which doesn’t work for my use case because the content is dynamic and the height can vary. Since I’m using SwiftData to manage my data, I need a way to keep this list fully dynamic. I’m open to using a VStack instead of a List to display the items, but I still need the reorder functionality that List provides. Does anyone know a good way to achieve this behavior? Is there a way to use onMove or similar functionality without needing a fixed height, or perhaps an alternative approach that might work better with SwiftData? Any suggestions or insights would be greatly appreciated! Thanks!
2
0
363
Sep ’24
SwiftData migration with iCloud and multi-device support
I’m writing test apps using SwiftData. Running migrations locally works fine. But I couldn’t find anything on how to handle a situation, where my local app is running on e.g. ‘MigrationSchemaV2’ and an iOS app is still running on ‘MigrationSchemaV1’ hence needs to be updated before the data migration takes place. I expect the local migration to be synced to iCloud automatically therefore the iOS app would crash. I’m looking for documentation, tutorials, best practice or lessons learned in order to understand the basic implementation idea, its dependencies and implications.
2
0
281
Aug ’24
modelContext.fetchIdentifiers(descriptor) Errors when Using a SortDescriptor
modelContext.fetchIdentifiers(descriptor) errors when using a SortDescriptor to sort by a variable and returns no models. The fetch works fine without a SortDescriptor, thus FetchDescriptor<MyModel>() works fine, but FetchDescriptor<MyModel>(sortBy: [.init(\.title)]) or FetchDescriptor<MyModel>(sortBy: [SortDescriptor(\.title)]) errors with console message The operation couldn’t be completed. (SwiftData.SwiftDataError error 1.) I am using Xcode Version 16.0 beta 6 (16A5230g).
1
0
367
Aug ’24
Unhandled exception finding default Directory URL '+[NSPersistentContainer defaultDirectoryURL] Could not conjure up a useful location for writing persistent stores.'
https://github.com/ordo-one/package-benchmark/issues/264 Hi! I am seeing this error specifically when I try to run the Ordo One benchmarks package with a SwiftData context. I am not sure if there is something missing in Ordo One or if this is some kind of legit SwiftData error. My benchmarks seem to be running fine even after the error prints. Any idea where that error might be coming from (and why I am not seeing that error when running SwiftData from other package executables)?
4
0
375
Aug ’24
Limitations for attributes in SwiftData models?
Hi, is there any description/documentation about what can not be used as SwiftData attributes? I do not mean things which cause issues at compile time, like having a type which is not Codeable. But rather I am looking for info which things to avoid, if you do not want to run into application crashes in modelContext.save(). Like for example having an enum which as an optional associated value as an attribute type (crashes on save is the associated value is nil). Anybody seen any documentation about that? Or tech notes? Thanks in advance for any hints :-). Cheers, Michael
1
1
291
Sep ’24
SwiftData does not persist change on relationship on iOS 18 beta 7
I came across of something I'm struggling to comprehend. I've got an iOS app based on SwiftUI and SwiftData + CloudKit. I wrote it using Xcode 15 and the target was iOS 17. Everything works fine in this environment, but after upgrading my phone to iOS 18 beta 7 something very strange started to happen with SwiftData on a physical device and in the simulator. Every time when data is updated, to be precise - when the relationship is modified, the change is reverted after 15 seconds! I've got the following settings on and nothing can be seen it's going on there in the logs -com.apple.CoreData.Logging.stderr 1 -com.apple.CoreData.CloudKitDebug 1 -com.apple.CoreData.SQLDebug 1 -com.apple.CoreData.ConcurrencyDebug 1 Here you are some simplified code extraction: @Model final public class Note: Identifiable, Hashable { public private(set) var uuid = UUID().uuidString var notification: Notification? ... } @Model final public class Notification: Identifiable, Hashable { var dateId: String = "" @Relationship(deleteRule: .nullify, inverse: \Note.notification) var notes: [Note]? init(_ dateId: String) { self.dateId = dateId } } @ModelActor final public actor DataModelActor : DataModel { public func updateNotification(oldDate: Date, newDate: Date? = nil, persistentModelId: PersistentIdentifier) { if let note = modelContext.model(for: persistentModelId) as? Note { updateNotification(oldDate: oldDate, newDate: newDate, note: note) } try? self.modelContext.save() } private func updateNotification(oldDate: Date? = nil, newDate: Date? = nil, note: Note) { if let oldDate = oldDate { let notifications = fetchNotifications() let oldDateId = NotificationDateFactory.getId(from: oldDate) // removing the note from the collection related to oldDate if let notification = notifications.first(where: { $0.dateId == oldDateId }) { if let notificationNotes = notification.notes { if let notificationNoteIndex = notification.notes!.firstIndex(of: note) { notification.notes!.remove(at: notificationNoteIndex) } if notification.notes == nil || notification.notes!.isEmpty { self.modelContext.delete(notification) } } } } if let newDate = newDate, newDate > Calendar.current.startOfToday() { // adding to a new collection related to newDate let notifications = fetchNotifications() let newDateId = NotificationDateFactory.getId(from: newDate) if let notification = notifications.first(where: { $0.dateId == newDateId }) { note.notification = notification } else { let notification = Notification(newDateId) note.notification = notification } } } } Spreading save method here and there does not help :( I've used Core Data Lab software to look into database and I can clearly see data changes are reverted for relationship property. Example: In Notification database there is one element: 2024-08-26 (3) with 3 notes attached. I modified one note to send notification on 2024-08-27. Changes in database reflects situation correctly showing: 2014-08-26 (2) 2024-08-27 (1) BUT!!! After 15 seconds doing noting database looks like this: 2024-08-26 (3) 2024-08-27 (0) All changes were reverted and all notes are still attached to the same date as they were at the first place. Any thoughts?
3
1
821
Aug ’24
SwiftUI and SwiftData @Query
Hey everyone, I’m relatively new to SwiftUI and iOS development, having started earlier this year, and I’m working on a Notes app using SwiftData. I’ve run into some issues when dealing with nested views. In my app, I’m using a SwiftData @Query in a parent view and then passing the model objects down the view tree. However, I’m frequently encountering errors such as “No container found” or similar. To work around this, I’ve tried having the child views perform their own SwiftData @Query, with the parent view passing a string identifier to the child views. This approach has helped somewhat, but I’m still not entirely sure how to properly manage UI updates. Additionally, I’ve noticed that turning on iCloud syncing seems to have made the problem worse. The errors have become more frequent, and it’s unclear to me how to handle these situations effectively. Could someone explain the best practices for handling SwiftData queries in nested SwiftUI views, especially with iCloud syncing enabled? Any guidance or feedback would be greatly appreciated. Thank you!
1
0
500
Aug ’24
How to write predicate to express not-in for compound index?
For example: SELECT * FROM accounts WHERE (platform, innerID) NOT IN ( ('platform_value1', 'innerID_value1'), ('platform_value2', 'innerID_value2'), ... ); this is hard to use Swift Predicate: func _fetchAccountNotIn(_ scope: [Account]) throws -> [Account] { let scope = scope.map{ ($0.platform, $0.innerID) } return try fetch(.init(predicate: #Predicate<Account> { !scope.contains(($0.platform, $0.innerID)) })) } shows compiler error: Cannot convert value of type '(String, String)' to expected argument type '((String, String)) throws -> Bool' Account definition: @Model public final class Account { #Unique<Account>([\.platform, \.innerID]) #Index<Account>([\.platform, \.innerID]) @Attribute(.preserveValueOnDeletion) public private(set) var platform : String @Attribute(.preserveValueOnDeletion) public private(set) var innerID : String }
1
0
325
Aug ’24
SwiftData error when trying to save array in Model
In my SwiftData @Model I have an attribute attachments: [String]? and I'm getting an error when trying to save it: CoreData: fault: Could not materialize Objective-C class named "Array" from declared attribute value type "Array<String>" of attribute named attachments. Getting this error periodically on iOS 18 Beta 7 on iPhone, but the same code works on iPadOS 18 b7 and on macOS Sonoma (14.6.1) Anyone seen this?
5
1
554
Sep ’24
error: the replacement path doesn't exist:
I'm using SwiftData with an @Model and am also using an @ModelActor. I've fixed all concurrency issues and have migrated to Swift 6. I am getting a console error that I do not understand how to clear. I get this error in Swift 6 and Swift 5. I do not experience any issue with the app. It seems to be working well. But I want to try to get all issues taken care of. I am using the latest Xcode beta. error: the replacement path doesn't exist: "/var/folders/1q/6jw9d6mn0gx1znh1n19z2v9r0000gp/T/swift-generated-sources/@_swiftmacro_17MyAppName14MyModelC4type18_PersistedPr> opertyfMa.swift"
21
8
4.2k
3w