Hello
By default if swiftData is unable to open a file on mac (due to it being the wrong format, or an old model) the app hard crashes (macOS 14.4.1) - is there a way to catch this problem and present a message to the user, rather than the app simply crashing
The whole way that swiftData opens files etc is very opaque so I'm not clear how we can wrap things in a nice way
Thanks
Richard
SwiftData
RSS for tagSwiftData 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
Hello
I have a swiftUI/swiftData document based app. In this I have created a singleton object using the folllowing in my ContentView struct:
@Query private var persistantStores:[PersistantStateManagerStorage]
@State private var stateManager: PersistantStateManagerStorage?
I then, call the following on the onAppear and onDisappear calls on the top level HStack{} item in my view
.onAppear(){
if let store = persistantStores.first
{
self.stateManager = store // data.first
} else
{
self.stateManager = PersistantStateManagerStorage()
modelContext.insert(stateManager!)
try? modelContext.save()
}
}.onDisappear(){
print("bye bye")
self.stateManager = nil
}
I then wrap my view inside a
if let stateManager = stateManager {}
block to unwrap the optional, and bind to values with a call such as
Stepper("Nudge Amount", value: Bindable(stateManager).nudgeAmount, in: 1...20)
I have to use Bindable(stateManager) rather than $stateManager as the $stateManager doesn't know its been unwrapped
All of this works fine until I close a document, at which point I get a crash with the message "SwiftData/BackingData.swift:124: Fatal error: Unable to get value - no backing Managed Object" which appears to happen when trying to access some of the properties of my persistantStore object (which has been retired)
I assume that my persistantStore object being an optional is part of the problem but I can't work out a better method of doing what I am trying to do
Perhaps I should not be asking this here, but can anyone recommend a good learning resource/tutorial for a complete beginner for SwiftData and CloudKit?
In any SwiftData project with undo enabled, I noticed that repeatedly undoing and redoing a change will cause memory to continuously climb. The larger the change, the more memory is consumed. This is surprising to me.
I confirmed this on iOS and macOS using Xcode 15.4 and 16 beta.
To reproduce, simply add undo/redo functionality to any project and repeatedly undo/redo a change. Memory consumption will climb continuously and will not get released.
In Paul Hudson's SwiftData tutorials, he has an entire section about the numerous tricks required to get SwiftData relationships to work correctly. Does anyone know if SwiftData also requires tricks to get the UndoManager to work correctly?
Hi, when setting up our Core Data stack in our iOS app (manually, with a NSManagedObjectModel/Context & NSPersistentStoreCoordinator) we have reports a rare bug we haven't been able to reproduce.
Occasionally when adding a persistent store we get a NSCocoaErrorDomain 256 error (NSFileReadUnknownError) with NSSQLiteErrorDomain=4618 in the user info. That's SQLITE_IOERR_SHMOPEN , which the SQLite docs describe this way:
I/O error within the xShmMap method on the sqlite3_io_methods object while trying to open a new shared memory segment
It seems to specifically /not/ be a SQLITE_FULL error.
Do you know what type of issue can cause this? Or where/how I can start tracking this down?
My main use case is that I have one data source that is obviously user data, and another data source that’s refereence materials shared by all users (but only a few users can mutate).
any insights would be greatly appreciated.
i am developing a SwiftUI App, where i need to work with relatively large amounts of data sets. While processing these data i had some issues with my app crashing randomly. As i was debugging this situation for a while i found out that dataraces were the cause for these crashes. That is why i decided to use an actor for these things..
As the actor takes care of concurrent threads, i was not having any crashes anymore, BUT, now i have to deal with some memory leaks!
i've created a simple demo project to reproduce these leaks.
my view:
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
// @Query private var items: [Item]
var body: some View {
VStack {
Button(action: {
Task { await testImport() }
}, label: {
Text("Import")
})
}
}
}
the function:
func testImport() async {
let actorX = testActor(container: self.modelContext.container)
await actorX.cleanUp()
// create dummy data:
var dummyArray: [Int] = []
for i in 0...1300 {
dummyArray.append(i)
}
await actorX.saveAssets(with: dummyArray)
dummyArray = []
print("Checkpoint")
}
the actor:
actor testActor {
public var modelContainer: ModelContainer
public var modelExecutor: any ModelExecutor
private var context: ModelContext { modelExecutor.modelContext }
public init(container: ModelContainer) {
self.modelContainer = container
let context = ModelContext(modelContainer)
modelExecutor = DefaultSerialModelExecutor(modelContext: context)
}
func cleanUp() {
print("starting cleanup...")
do {
try context.delete(model: Item.self)
print("cleanup: table LocalAsset truncated")
} catch {
print("Failed to clear all LocalAsset data.")
}
}
func saveAssets(with array: [Int]) {
for i in 0..<array.count {
let foo = array[i]
let newItem = Item(timestamp: Date(), dummyInt: foo)
context.insert(newItem)
}
try? context.save()
}
}
And Here's a screenshot of Xcode's Instruments Leak tool:
i hope somebody has any idea how to get rid of those leaks..
I have watched the following WWDC 2024 sessions:
What’s new in SwiftData
Create a custom data store with SwiftData
Platform State of the Union
Now, I have an application that exposes a GraphQL API endpoint that's using PostgreSQL Server 16.3 database. Next, this API returns JSON to the client application (i.e. SwiftUI app). Furthermore, I have checked the current documentation and the above videos appear to be the best reference at this time.
My proposed architecture looks like the following:
SwiftUI <-->
SwiftData <-->
PostgreStoreConfiguration && PostgreStore TBD <-->
GraphQL API <-->
PostgreSQL
Thus, I have the following questions:
Are there plans to add common out-of-the-box data store implementations for PostgreSQL, Cassandra, Redis, and so on?
Will it be possible to implement data stores built to use gRPC, GraphQL, REST, and others to name a few?
Will there be more documentation on the actual creation of a custom data store because the current documentation provides a slim API reference?
I look forward to your feedback regarding the SwiftData custom data stores.
I've just tried to update a project that uses SwiftData to Swift 6 using Xcode 16 beta 1, and it's not working due to missing Sendable conformance on a couple of types (MigrationStage and Schema.Version):
struct LocationsMigrationPlan: SchemaMigrationPlan {
static let schemas: [VersionedSchema.Type] = [LocationsVersionedSchema.self]
static let stages: [MigrationStage] = []
}
struct LocationsVersionedSchema: VersionedSchema {
static let models: [any PersistentModel.Type] = [
Location.self
]
static let versionIdentifier = Schema.Version(1, 0, 0)
}
This code results in the following errors:
error: static property 'stages' is not concurrency-safe because non-'Sendable' type '[MigrationStage]' may have shared mutable state
static let stages: [MigrationStage] = []
^
error: static property 'versionIdentifier' is not concurrency-safe because non-'Sendable' type 'Schema.Version' may have shared mutable state
static let versionIdentifier = Schema.Version(1, 0, 0)
^
Am I missing something, or is this a bug in the current seed? I've filed this as FB13862584.
I'm trying out SwiftData and converting my existing data models using Structs and Codable to Classes and SwiftData. I've got a model that has a Measurement<UnitMass> type. When I run the app after converting everything to use classes with the @Model macro, I get a fatal error: SwiftData/SchemaProperty.swift:325: Fatal error: Unexpected type for CompositeAttribute: NSUnitMass.
Since Measurement conforms to Codable, and SwiftData should work with Codable types, why is this not working?
I also tried marking the property with @Attribute(.transformable) and it didn't make a difference.
I was hoping for an update of SwiftData which adopted the use of shared and public CloudKit containers, in the same way it does for the private CloudKit container.
So firstly, a big request to any Apple devs reading, for this to be a thing!
Secondly, what would be a sensible way of adding a shared container in CloudKit to an existing app that is already using SwiftData?
Would it be possible to use the new DataStore method to manage CloudKit syncing with a public or shared container?
I just saw the new SwiftData updates, including the DataStore API. I’m wondering if, in the case of a shared remote datastore, it is possible to enable updates to the model context from the datastore without the model context explicitly requesting them (e.g., through database listeners).
If I’m not mistaken, when you use CloudKit with SwiftData, this happens, right?
I'm currently using Xcode 16 Beta (16A5171c) and I'm getting a crash whenever I attempt to fetch using my ModelContext in my SwiftUI video using the environment I'm getting a crash specifically on iOS 18 simulators.
I've opened up a feedback FB13831520 but it's worth noting that I can run the code I'll explain in detail below on iOS 17+ simulator and devices just fine.
I'm getting the following crash:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'The specified URI is not a valid Core Data URI: x-coredata:///MyApp/XXXXX-XXXXX-XXXX-XXXX-XXXXXXXXXXXX'
It's almost as if on iOS18 SwiftData is unable to find the file on the simulator to perform CRUD operations.
All I'm doing in my project is simply fetching data using the modelContext.
func contains(_ model: MyModel, in context: ModelContext) -> Bool {
let objId = palette.persistentModelID
let fetchDesc = FetchDescriptor<MyModel>(predicate: #Predicate { $0.persistentModelID == objId })
let itemCount = try? context.fetchCount(fetchDesc)
return itemCount != 0
}
How do I access the SwiftData ModelContainer of my app inside the widget extension?
Following the guide to add a Widget Extension I’ve added the necessary target to my project and enabled App Groups.
I’ve seen that the Backyard Birds example code offers an example how to build this, but it encapsulates the SwiftData handling in a separate app package. Therefore the example simply points to the app package. Though, in my case I don’t host my SwiftData in an external app package, but simply inside the regular app target.
So, my question is how and at which point in code do I need to access the ModelContainer?
in the SampleTrips, for the pure SwiftData version, when I add a living accommodation from the trip detail view, the trip detail view won’t update until I go back to the trip list view and enter again.
why didn’t the trip detail view update?
I have tested the pure CoreData version in sample code, it worked well.
is this a bug or a feature of SwiftData?
The following videos from WWDC24 say that we can use cloud storage for our SwiftData stores. Video references below:
What's new in SwiftData
Create a custom data store with SwiftData
When watching the videos it shows examples of how we can read and write to a JSON file as a store, rather than using the built in store that comes with SwiftData.
But there are mentions that we can use cloud storage like a backend i.e. a database hosted on a server. The only thing that I'm struggling to figure out and it would be great if we had code samples for this is how to achieve using cloud storage as a store since looking at the current implementations of:
DataStoreConfiguration
DataStore
The required functions we need to implement look synchronous rather than asynchronous so how would or could we handle fetching asynchronous data from cloud storage. So how would we handle this?
Also it would be great if someone could clarify how or if there is a way to send notifications for changes to your store between different devices similar to CloudKit?
Are there there any plans to provide more documentation & sample code for the questions I've asked above?
Feedback FB13857743
I have a simple model with a unique field.
@Model
class M {
@Attribute(.unique)
var value: String
var date: Date
var answer: Int
init(value: String, date: Date = .now, answer: Int = 0) {
self.value = value
self.date = date
self.answer = answer
}
}
I am creating new objects with
let x = M(value: "x")
modelContext.insert(x)
The value field has a unique constraint, and I am observing these differences in iOS 18 beta (Xcode 16.0 beta) from iOS 17.5 (Xcode 15.4):
Multiple objects with the same value field appear in the list.
If explicit modelContext.save() is called, list is not updated with latest values.
Is this something I need to adjust to, or beta issues?
Full source code: https://github.com/paiv/swiftdata-insert-unique-1
When deleting the last added item from a list view in my app a bar chart in a different view crashes my app. If I delete any other item in the list view everything work as expected. I'm using SwiftData in my app.
Does anyone have any idea how I can prevent the app from crashing?
I filter the data in the init to only have the current days data
Chart View
struct ConsumedDrinkChartView: View {
@Environment(\.modelContext) var modelContext
let screenVerticalSizeClass = UIScreen.VerticalSizeClass
var compactScreen: Bool {
return screenVerticalSizeClass == "compact"
}
@State private var chartCalendarUnit: Calendar.Component = .hour
@State private var chartRange: ClosedRange<Date>
@State private var axisValueLabelFormat: Date.FormatStyle
@State private var axisValueLabelCount: Int
@State private var startDate: Date
@State private var endDate: Date
@State private var plotStartPadding: Double = 0
@State private var plotEndPadding: Double = 0
@Binding var selectedTimeFrame:String
@Query var consumedFluids: [ConsumedDrink]
let defaultVolume = DataStore.defaultVolume
init(selectedTimeFrame: Binding<String>, dateRange: ClosedRange<Date>) {
_selectedTimeFrame = selectedTimeFrame
_startDate = State(initialValue: Date().startOfDay)
_endDate = State(initialValue: Date().endOfDay)
let endDate = dateRange.upperBound
let startDate = dateRange.lowerBound
_consumedFluids = Query(filter: #Predicate {
$0.date > startDate && $0.date < endDate
}, sort: \ConsumedDrink.date)
_chartRange = State(initialValue: dateRange)
_axisValueLabelFormat = State(initialValue: .dateTime.hour(.conversationalDefaultDigits(amPM: .narrow)))
_axisValueLabelCount = State(initialValue: 2)
}
var body: some View {
Chart {
ForEach(consumedFluids) { consumedFluid in
BarMark(x: .value("Date", consumedFluid.date, unit: chartCalendarUnit),
y: .value("Fluid Ounces", consumedFluid.drink.amount))
}
.foregroundStyle(.pink)
}
.frame(height: 180)
.padding()
.chartXAxis {
AxisMarks(values: .stride(by: chartCalendarUnit, count: axisValueLabelCount,roundLowerBound: true, roundUpperBound: true)) { _ in
AxisGridLine()
AxisValueLabel(format: axisValueLabelFormat, centered: true)
}
}
.chartXScale(domain: chartRange, range: .plotDimension(startPadding: plotStartPadding, endPadding: plotEndPadding))
.background(RoundedRectangle(cornerRadius: 12).fill(Color(.secondarySystemBackground)))
.onChange(of: selectedTimeFrame) {
selectChartRange()
}
.onChange(of: consumedFluids) {
print("consumedFluids: \(consumedFluids.count)")
}
.onAppear {
selectChartRange()
}
}
func selectChartRange() {
plotStartPadding = 0
plotEndPadding = 0
switch selectedTimeFrame {
case "Day":
startDate = Date().startOfDay
endDate = Date().endOfDay
chartCalendarUnit = .hour
axisValueLabelCount = 2
axisValueLabelFormat = .dateTime.hour(.conversationalDefaultDigits(amPM: .narrow))
case "Week":
startDate = Date().add(days: -7)
chartCalendarUnit = .day
axisValueLabelCount = 1
axisValueLabelFormat = .dateTime.weekday()
case "Month":
startDate = Date().add(days: -30)
chartCalendarUnit = .day
axisValueLabelCount = 2
axisValueLabelFormat = .dateTime.day()
plotStartPadding = 10
plotEndPadding = 10
case "SixMonths":
let endOfMonth = Date().endOfMonth()
startDate = endOfMonth.add(months: -6)
chartCalendarUnit = .month
axisValueLabelCount = 1
axisValueLabelFormat = .dateTime.month()
plotStartPadding = 10
plotEndPadding = 32
case "Year":
let endOfMonth = Date().endOfMonth()
startDate = endOfMonth.add(months: -12)
chartCalendarUnit = .month
axisValueLabelCount = 1
axisValueLabelFormat = .dateTime.month(.narrow)
plotStartPadding = 15
plotEndPadding = 15
default:
chartCalendarUnit = .day
}
chartRange = startDate...endDate
}
}
List View
struct ConsumedDrinkListView: View {
@Environment(\.modelContext) var modelContext
@Query(sort: \ConsumedDrink.date) var dailyConsumedFluids: [ConsumedDrink]
@State private var showingAlert = false
@State private var alertMessage: String = ""
@State private var alertTitle: String = ""
var body: some View {
NavigationStack {
if dailyConsumedFluids.isEmpty {
ContentUnavailableView("No Consumed Drinks", systemImage: "mug.fill", description: Text("Drink some water and stay hydrated."))
} else {
List {
ForEach(dailyConsumedFluids, id: \.self) { consumedDrink in
NavigationLink {
EditConsumedDrinkView(consumedDrink: consumedDrink)
} label: {
ConsumedDrinkRowView(consumedDrink: consumedDrink)
}
.swipeActions{
Button("Delete", systemImage: "trash", role: .destructive) {
deleteConsumedDrink(consumedDrink: consumedDrink)
}
.tint(.red)
}
}
}
.listStyle(.plain)
.alert(isPresented: $showingAlert) {
Alert(title: Text(alertTitle),
message: Text(alertMessage),
dismissButton: .default(Text("OK"))
)
}
}
Text("")
.navigationTitle("Consumed Drinks")
.navigationBarTitleDisplayMode(.inline)
}
}
func deleteConsumedDrink(consumedDrink: ConsumedDrink) {
do {
if modelContext.hasChanges {
print("ConsumedDrinkListView.deleteConsumedDrink")
print("modelContext has Changes. Saving modelContext")
try modelContext.save()
}
try DataStore.deleteConsumedDrink(drink: consumedDrink, modelContext: modelContext)
} catch {
self.alertTitle = "Error deleting consumed drink - \(consumedDrink.drink.name)"
self.alertMessage = error.localizedDescription
self.showingAlert = true
}
}
}
The exact error is: Unable to create bundle at URL (file:///System/Library/CoreServices/SystemVersion.bundle): does not exist or not a directory (0).
SwiftData offers a #Unique macro as of iOS 18, see here: https://developer.apple.com/documentation/swiftdata/unique(_:)
I was wondering if that one might work when the DB is backed by CloudKit.