I'm implementing SwiftData with inheritance in an app.
I have an Entity class with a property name. This class is inherited by two other classes: Store and Person. The Entity model has a one-to-many relationship with a Transaction class.
I can list all my Entity models in a List with a @Query annotation without a problem.
However, then I try to access the name property of an Entity from a Transaction relationship, the app crashes with the following error:
Thread 1: Fatal error: Never access a full future backing data - PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(backing: SwiftData.PersistentIdentifier.PersistentIdentifierBacking.managedObjectID(0x96530ce28d41eb63 <x-coredata://DABFF7BB-C412-474E-AD50-A1F30AC6DBE9/Person/p4>))) with Optional(F07E7E23-F8F0-4CC0-B282-270B5EDDC7F3)
From my attempts to fix the issue, I noticed that:
The crash seems related to the relationships with classes that has inherit from another class, since it only happens there.
When I create new data, I can usually access it without any problem. The crash mostly happens after reloading the app.
This error has been mentioned on the forum (for example here), but in a context not related with inheritance.
You can find the full code here.
For reference, my models looks like this:
@Model
class Transaction {
@Attribute(.unique)
var id: String
var name: String
var date: Date
var amount: Double
var entity: Entity?
var store: Store? { entity as? Store }
var person: Person? { entity as? Person }
init(
id: String = UUID().uuidString,
name: String,
amount: Double,
date: Date = .now,
entity: Entity? = nil,
) {
self.id = id
self.name = name
self.amount = amount
self.date = date
self.entity = entity
}
}
@Model
class Entity: Identifiable {
@Attribute(.preserveValueOnDeletion)
var name: String
var lastUsedAt: Date
@Relationship(deleteRule: .cascade, inverse: \Transaction.entity)
var operations: [Transaction]
init(
name: String,
lastUsedAt: Date = .now,
operations: [Transaction] = [],
) {
self.name = name
self.lastUsedAt = lastUsedAt
self.operations = operations
}
}
@available(iOS 26, *)
@Model
class Store: Entity {
@Attribute(.unique) var id: String
var locations: [Location]
init(
id: String = UUID().uuidString,
name: String,
lastUsedAt: Date = .now,
locations: [Location] = [],
operations: [Transaction] = []
) {
self.locations = locations
self.id = id
super.init(name: name, lastUsedAt: lastUsedAt, operations: operations)
}
}
In order to reproduce the error:
Run the app in the simulator.
Click the + button to create a new transaction.
Relaunch the app, then click on any transaction.
The app crashes when it tries to read te name property while building the details view.
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
Good morning everyone!
Today I have a question about using SwiftData with CloudKit and Widgets. I recently set up my project for SwiftData and CloudKit synchronization, but for some reason, I’m not able to give my Widget access to this data. CloudKit works perfectly fine for my main app, but the Widget only shows placeholder data(the placeholder data which were defined in the get functions as catch, this is sure).
I have set the CloudKit capability for my Widget extension and tried fetching data with the get-functions in the code below. I also ensured that the data model files are members of the Widget extension target and that the Widget extension uses the same CloudKit container as the main app.
I wondered if it is possible and reasonable to save a copy of my CloudKit data in an App Group container, but in that case, the information shown in the Widget are not always up-to-date, so a solution that fetches data directly from CloudKit would be better. Has anyone had experience with this case? I couldn’t find much information about this problem online.
In the code below, many parts have been deleted or altered because they are not relevant to the problem, as they don’t fetch data. The variables, functions, and data models in the code may sometimes have German names, but I hope you can still understand it.
Thanks for your help!
struct Provider: AppIntentTimelineProvider {
//[Placeholder and snapshot]
func timeline(for configuration: ConfigurationAppIntent, in context: Context) async -> Timeline<CleverEntry> {
let entry = await loadAllVariables()
return Timeline(entries: [entry], policy: .after(Date().addingTimeInterval(60 * 5)))
}
@MainActor
private func getExam() -> [PruefungM] {
//Old, local version
/*
guard let modelContainer = try? ModelContainer(for: PruefungM.self) else {
return []
}
let descriptor = FetchDescriptor<PruefungM>()
let PRF = try? modelContainer.mainContext.fetch(descriptor)
return PRF ?? []
*/
do {
let configuration = ModelConfiguration(cloudKitDatabase: .private("iCloud.my_bundle_id"))
let container = try ModelContainer(
for: PruefungM.self,
configurations: configuration
)
let descriptor = FetchDescriptor<PruefungM>()
return try container.mainContext.fetch(descriptor)
} catch {
print("❌ Error(CloudKit): \(error)")
return []
}
}
@MainActor
private func getHAF() -> [HausaufgabeM] {
do {
let configuration = ModelConfiguration(cloudKitDatabase: .private("iCloud.my_bundle_id"))
let container = try ModelContainer(
for: HausaufgabeM.self,
configurations: configuration
)
let descriptor = FetchDescriptor<HausaufgabeM>()
return try container.mainContext.fetch(descriptor)
} catch {
print("❌ Error (CloudKit): \(error)")
return []
}
}
@MainActor
private func loadAllVariables() -> CleverEntry {
print("Function started")
let HAF = getHAF()
let PRF = getExam()
//handling and returning the data
}
}
Hello everyone,
I'm developing an app for iOS 18 using SwiftData, with iCloud synchronization enabled. My app also includes an interactive widget that allows users to mark tasks as complete, similar to Apple's Reminders widget.
I'm facing a specific issue with how iCloud sync is triggered when changes are made from the widget.
My Setup:
Xcode 16
Swift 6 / iOS 18
SwiftData with iCloud Sync enabled.
An interactive widget using App Intents to modify the SwiftData model.
What's working correctly:
App to Widget (Same Device): If I mark a task as complete in the main app, the widget on the same device updates instantly.
Widget to App (Same Device): If I mark a task as complete using the interactive widget, the main app on the same device reflects this change immediately.
App to App (Across Devices): If I make a change in the app on my iPhone, it syncs correctly via iCloud and appears in the app on my iPad.
The Problem:
The synchronization issue occurs specifically when an action is initiated from the widget and needs to be reflected on other devices, or when a change from another device needs to be reflected in the widget.
Widget Change Not Syncing to Other Devices: If I mark a task as complete in the widget on my iPhone, the change does not sync to my iPad. The task on the iPad only updates after I manually open the main app on the iPhone.
Remote Change Not Syncing to Widget: Similarly, if I mark a task as complete in the app on my iPad, the widget on my iPhone does not update to show this change. The widget only refreshes with the correct state after I open the main app on the iPhone.
It seems that the widget's AppIntent correctly modifies the local SwiftData store, but this action isn't triggering the necessary background process to push the changes to iCloud. The sync only seems to happen when the main app, with its active ModelContainer, is brought to the foreground.
My goal is for any change made in the widget to be reflected across all of the user's devices in near real-time, without requiring them to launch the main app to initiate the sync.
Is there a specific API I need to call from my AppIntent to force a SwiftData sync, or a project capability I might be missing to allow the widget extension to trigger iCloud pushes?
Any help or guidance would be greatly appreciated.
Thank you!
In iOS 26, should we have bootloader that runs the repo on startup - or should we have that inside tasks in root view?
we have repos that runs as a «closed» functions, we dont throw but updates swiftdata and we use @query in the views. So what is best?
and for the repo we should have a repo that runs the upserts manage relations eg? Should that run on a modelactor?
I'm trying to set up an application using SwiftData to have a number of models backed by a local datastore that's not synced to CloudKit, and another set of models that is. I was able to achieve this previously with Core Data using multiple NSPersistentStoreDescription instances.
The set up code looks something like:
do {
let fullSchema = Schema([
UnsyncedModel.self,
SyncedModel.self,
])
let localSchema = Schema([UnsyncedModel.self])
let localConfig = ModelConfiguration(schema: localSchema, cloudKitDatabase: .none)
let remoteSchema = Schema([SyncedModel.self])
let remoteConfig = ModelConfiguration(schema: remoteSchema, cloudKitDatabase: .automatic)
container = try ModelContainer(for: fullSchema, configurations: localConfig, remoteConfig)
} catch {
fatalError("Failed to configure SwiftData container.")
}
However, it doesn't seem to work as expected. If I remove the synced/remote schema and configuration then everything works fine, but the moment I add in the remote schema and configuration I get various different application crashes. Some examples below:
A Core Data error occurred." UserInfo={Reason=Entity named:... not found for relationship named:...,
Fatal error: Failed to identify a store that can hold instances of SwiftData._KKMDBackingData<...>
Has anyone ever been able to get a similar setup to work using SwiftData?
I just made a small test app that uses SwiftData with CloudKit capability. I created a simple Book model as seen below. It looks like enums and structs when used with CloudKit capability all trigger this error:
'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
I fixed the error by using genreRaw String and using a computed property to use it in the app, but it popped back up after adding the ReadingProgress struct
Should I ignore the error and assume Apple still supports enums and codable structs when using SwiftData with CloudKit?
import SwiftData
@Model
class Book {
var title: String = ""
var author: String = ""
var genreRaw: String = Genre.fantasy.rawValue
var review: String = ""
var rating: Int = 3
var progress: ReadingProgress?
var genre: Genre {
get { Genre(rawValue: genreRaw) ?? Genre.fantasy }
set { genreRaw = newValue.rawValue }
}
init(title: String, author: String, genre: Genre, review: String, rating: Int, progress: ReadingProgress? = nil) {
self.title = title
self.author = author
self.genre = genre
self.review = review
self.rating = rating
self.progress = progress
}
}
struct ReadingProgress: Codable {
var currentPage: Int
var totalPages: Int
var isFinished: Bool
var percentComplete: Double {
guard totalPages > 0 else { return 0 }
return Double(currentPage) / Double(totalPages) * 100
}
}
enum Genre: String, Codable, CaseIterable {
case fantasy
case scienceFiction
case mystery
case romance
var displayName: String {
switch self {
case .fantasy:
return "Fantasy"
case .scienceFiction:
return "Science Fiction"
case .mystery:
return "Mystery"
case .romance:
return "Romance"
}
}
}
I'm developing an app that uses CloudKit synchronization with SwiftData and on visionOS I added an App Settings bundle. I have noticed that sometimes, when the app is open and the user changes a setting from the App Settings bundle, the following fatal error occurs:
SwiftData/BackingData.swift:831: Fatal error: This model instance was destroyed by calling ModelContext.reset and is no longer usable.
The setting is read within the App struct in the visionOS app target using @AppStorage and this value is in turn used to set the passthrough video dimming via the .preferredSurroundingsEffect modifier. The setting allows the user to specify the dimming level as dark, semi dark, or ultra dark.
The fatal error appears to occur intermittently although the first time it was observed was after adding the settings bundle. As such, I suspect there is some connection between those code changes and this fatal error even though they do not directly relate to SwiftData.
I have SwiftData models containing arrays of Codable structs that worked fine before adding CloudKit capability. I believe they are the reason I started seeing errors after enabling CloudKit.
Example model:
@Model
final class ProtocolMedication {
var times: [SchedulingTime] = [] // SchedulingTime is Codable
// other properties...
}
After enabling CloudKit, I get this error logged to the console:
'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
CloudKit Console shows this times data as "plain text" instead of "bplist" format.
Other struct/enum properties display correctly (I think) as "bplist" in CloudKit Console.
The local SwiftData storage handled these arrays fine - this issue only appeared with CloudKit integration.
What's the recommended approach for storing arrays of Codable structs in SwiftData models that sync with CloudKit?
I have an app that from day 1 has used Swiftdata and successfully sync'd across devices with Cloudkit. I have added models to the data in the past and deployed the schema and it continued to sync across devices. Sometime I think in June.2025 I added a new model and built out the UI to display and manage it. I pushed a version to Test Flight (twice over a matter of 2 versions and a couple of weeks) and created objects in the new model in Test Flight versions of the app which should push the info to Cloudkit to update the schema.
When I go to deploy the schema though there are no changes. I confirmed in the app that Cloudkit is selected and it's point to the correct container. And when I look in Cloudkit the new model isn't listed as an indes.
I've pushed deploy schema changes anyway (more than once) and now the app isn't sync-ing across devices at all (even the pre-existing models aren't sync-ing across devices).
I even submitted the first updated version to the app store and it was approved and released. I created objects in the new model in production which I know doesn't create the indexes in the development environment. But this new model functions literally everywhere except Cloudkit and I don't know what else to do to trigger an update.
have a SwiftUI View where I can edit financial transaction information. The data is stored in SwiftData. If I enter a TextField element and start typing, it is super laggy and there are hangs of 1-2 seconds between each input (identical behaviour if debugger is detached). On the same view I have another TextField that is just attached to a @State variable of that view and TextField updates of that value work flawlessly. So somehow the hangs must be related to my SwiftData object but I cannot figure out why.
This used to work fine until a few months ago and then I could see the performance degrading.
I have noticed that when I use a placeholder variable like
@State private var transactionSubject: String = ""
and link that to the TextField, the performance is back to normal. I am then using
.onSubmit {
self.transaction.subject = self.transactionSubject
}
to update the value in the end but this again causes a 1 s hang. :/
Below the original code sample with some unnecessary stuff removed:
struct EditTransactionView: View {
@Environment(\.modelContext) var modelContext
@Environment(\.dismiss) var dismiss
@State private var testValue: String = ""
@Bindable var transaction: Transaction
init(transaction: Transaction) {
self.transaction = transaction
let transactionID = transaction.transactionID
let parentTransactionID = transaction.transactionMasterID
_childTransactions = Query(filter: #Predicate<Transaction> {item in
item.transactionMasterID == transactionID
}, sort: \Transaction.date, order: .reverse)
_parentTransactions = Query(filter: #Predicate<Transaction> {item in
item.transactionID == parentTransactionID
}, sort: \Transaction.date, order: .reverse)
print(_parentTransactions)
}
//Function to keep text length in limits
func limitText(_ upper: Int) {
if self.transaction.icon.count > upper {
self.transaction.icon = String(self.transaction.icon.prefix(upper))
}
}
var body: some View {
ZStack {
Form{
Section{
//this one hangs
TextField("Amount", value: $transaction.amount, format: .currency(code: Locale.current.currency?.identifier ?? "USD"))
//this one works perfectly
TextField("Test", text: $testValue)
HStack{
TextField("Enter subject", text: $transaction.subject)
.onAppear(perform: {
UITextField.appearance().clearButtonMode = .whileEditing
})
Divider()
TextField("Select icon", text: $transaction.icon)
.keyboardType(.init(rawValue: 124)!)
.multilineTextAlignment(.trailing)
}
}
}
.onDisappear(){
if transaction.amount == 0 {
// modelContext.delete(transaction)
}
}
.onChange(of: selectedItem, loadPhoto)
.navigationTitle("Transaction")
.navigationBarTitleDisplayMode(.inline)
.toolbar{
Button("Cancel", systemImage: "trash"){
modelContext.delete(transaction)
dismiss()
}
}
.sheet(isPresented: $showingImagePickerView){
ImagePickerView(isPresented: $showingImagePickerView, image: $image, sourceType: .camera)
}
.onChange(of: image){
let data = image?.pngData()
if !(data?.isEmpty ?? false) {
transaction.photo = data
}
}
.onAppear(){
cameraManager.requestPermission()
setDefaultVendor()
setDefaultCategory()
setDefaultGroup()
}
.sheet(isPresented: $showingAmountEntryView){
AmountEntryView(amount: $transaction.amount)
}
}
}
}
If Cloudkit is enabled, SwiftData @Query operation hangs when the View scenePhase becomes active.
Seems like the more @Query calls you have, the more it hangs.
This has been first documented some time ago, but in typical Apple style, it has not been addressed or even commented on.
https://developer.apple.com/forums/thread/761434
I really enjoyed using SwiftData for persistence until I found out that the CloudKit integration ensures changes are only eventually consistent, and that changes can propagate to other devices after as long as minutes, making it useless for any feature that involves handoff between devices. Devastating news but I guess it’s on me for nrtfm. I may try my hand at a custom model context DataStore integrating Powersync, but that’s a whole trip and before I embark on it I was wondering if anyone had suggestions for resolving this problem in a simple and elegant manager that allows me to keep as much of the machinery within Apple’s ecosystem as possible, while ensure reliable “live” updates to SwiftData stores on all eligible devices.
I have an iOS app with a main target and an app extension. I want the app extension to write data to the SwiftData in App Groups, while the main target reads the data. However, currently, the app extension only has permission to read data, not to write it.Is the issue due to my incorrect configuration or an Apple-imposed restriction?
This simple test fails in my project. Similar code in my application also crashes.
How do I debug the problem?
What project settings are required. I have added SwiftData as a framework to test (and application) targets?
Thanks,
The problem is with:
modelContext.insert(item)
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
import XCTest
import SwiftData
@Model
class FakeModel {
var name: String
init(name: String) { self.name = name }
}
@MainActor
final class FakeModelTests: XCTestCase {
var modelContext: ModelContext!
override func setUp() {
super.setUp()
do {
let container = try ModelContainer(for: FakeModel.self, configurations: ModelConfiguration(isStoredInMemoryOnly: true))
modelContext = container.mainContext
} catch {
XCTFail("Failed to create ModelContainer: \(error)")
modelContext = nil
}
}
func testSaveFetchDeleteFakeItem() {
guard let modelContext = modelContext else {
XCTFail("ModelContext must be initialized")
return
}
let item = FakeModel(name: "Test")
modelContext.insert(item)
let fetchDescriptor = FetchDescriptor<FakeModel>()
let items = try! modelContext.fetch(fetchDescriptor)
XCTAssertEqual(items.count, 1)
XCTAssertEqual(items.first?.name, "Test")
modelContext.delete(item)
let itemsAfterDelete = try! modelContext.fetch(fetchDescriptor)
XCTAssertEqual(itemsAfterDelete.count, 0)
}
}
Hi,
I am currently experiencing some trouble when using parent model property in a predicate of a child model.
I have an Item class that define parent-child relationship:
@Model class Item {
var timestamp: Date
@Relationship(inverse: \Item.children)
var parent: Item?
var children: [Item]
init(parent: Item? = nil, children: [Item] = [], timestamp: Date = .now) {
self.parent = parent
self.children = children
self.timestamp = timestamp
}
}
I subclass this model like that:
@available(iOS 26, *)
@Model final class CollectionItem: Item { /* ... */ }
When i make a Query in my View like that the system crashes:
@Query(
filter: #Predicate<CollectionItem> { $0.parent == nil },
sort: \CollectionItem.name,
)
private var collections: [CollectionItem]
CrashReportError: Fatal Error in DataUtilities.swift
AppName crashed due to fatalError in DataUtilities.swift at line 85.
Couldn't find \CollectionItem.<computed 0x000000034005d4e8 (Optional<Item>)> on CollectionItem with fields [SwiftData.Schema.PropertyMetadata(name: "name", keypath: \CollectionItem.<computed 0x000000034003c120 (String)>, defaultValue: nil, metadata: nil), SwiftData.Schema.PropertyMetadata(name: "icon", keypath: \CollectionItem.<computed 0x000000034003ca04 (Optional<String>)>, defaultValue: nil, metadata: nil), SwiftData.Schema.PropertyMetadata(name: "timestamp", keypath: \Item.<computed 0x0000000340048018 (Date)>, defaultValue: nil, metadata: nil), SwiftData.Schema.PropertyMetadata(name: "parent", keypath: \Item.<computed 0x0000000340048a4c (Optional<Item>)>, defaultValue: nil, metadata: Optional(Relationship - name: , options: [], valueType: Any, destination: , inverseName: nil, inverseKeypath: Optional(\Item.<computed 0x0000000340048fe8 (Array<Item>)>))), SwiftData.Schema.PropertyMetadata(name: "children", keypath: \Item.<computed 0x0000000340048fe8 (Array<Item>)>, defaultValue: nil, metadata: nil)]
When I query as Item it works but then i cannot sort on CollectionItem field and must add unnecessary down casting:
@Query(
filter: #Predicate<Item> { $0.parent == nil && $0 is CollectionItem },
)
private var items: [Item]
Am I missing something? Is it a platform limitation or a known issue?
What is the correct way to track the number of items in a relationship using SwiftData and SwiftUI?
Imagine a macOS application with a sidebar that lists Folders and Tags. An Item can belong to a Folder and have many Tags. In the sidebar, I want to show the name of the Folder or Tag along with the number of Items in it.
I feel like I'm missing something obvious within SwiftData to wire this up such that my SwiftUI views correctly updated whenever the underlying modelContext is updated.
// The basic schema
@Model final class Item {
var name = "Untitled Item"
var folder: Folder? = nil
var tags: [Tag] = []
}
@Model final class Folder {
var name = "Untitled Folder"
var items: [Item] = []
}
@Model final class Tag {
var name = "Untitled Tag"
var items: [Item] = []
}
// A SwiftUI view to show a Folder.
struct FolderRowView: View {
let folder: Folder
// Should I use an @Query here??
// @Query var items: [Item]
var body: some View {
HStack {
Text(folder.name)
Spacer()
Text(folder.items.count.formatted())
}
}
}
The above code works, once, but if I then add a new Item to that Folder, then this SwiftUI view does not update. I can make it work if I use an @Query with an #Predicate but even then I'm not quite sure how the #Predicate is supposed to be written. (And it seems excessive to have an @Query on every single row, given how many there could be.)
struct FolderView: View {
@Query private var items: [Item]
private var folder: Folder
init(folder: Folder) {
self.folder = folder
// I've read online that this needs to be captured outside the Predicate?
let identifier = folder.persistentModelID
_items = Query(filter: #Predicate { link in
// Is this syntax correct? The results seem inconsistent in my app...
if let folder = link.folder {
return folder.persistentModelID == identifier
} else {
return false
}
})
}
var body: some View {
HStack {
Text(folder.name)
Spacer()
// This mostly works.
Text(links.count.formatted())
}
}
}
As I try to integrate SwiftData and SwiftUI into a traditional macOS app with a sidebar, content view and inspector I'm finding it challenging to understand how to wire everything up.
In this particular example, tracking the count, is there a "correct" way to handle this?
I have a UIKit app where I've adopted SwiftData and I'm struggling with a crash coming in from some of my users. I'm not able to reproduce it myself and as it only happens to a small fraction of my user base, it seems like a race condition of some sort.
This is the assertion message:
SwiftData/DefaultStore.swift:453: Fatal error: API Contract Violation: Editors must register their identifiers before invoking operations on this store SwiftData.DefaultStore: 00CF060A-291A-4E79-BEC3-E6A6B20F345E did not. (ID is unique per crash)
This is the ModelActor that crashes:
@available(iOS 17, *)
@ModelActor
actor ConsumptionDatabaseStorage: ConsumptionSessionStorage {
struct Error: LocalizedError {
var errorDescription: String?
}
private let sortDescriptor = [SortDescriptor(\SDConsumptionSession.startTimeUtc, order: .reverse)]
static func createStorage(userId: String) throws -> ConsumptionDatabaseStorage {
guard let appGroupContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: UserDefaults.defaultAppGroupIdentifier) else {
throw Error(errorDescription: "Invalid app group container ID")
}
func createModelContainer(databaseUrl: URL) throws -> ModelContainer {
return try ModelContainer(for: SDConsumptionSession.self, SDPriceSegment.self, configurations: ModelConfiguration(url: databaseUrl))
}
let databaseUrl = appGroupContainer.appendingPathComponent("\(userId).sqlite")
do {
return self.init(modelContainer: try createModelContainer(databaseUrl: databaseUrl))
} catch {
// Creating the model storage failed. Remove the database file and try again.
try? FileManager.default.removeItem(at: databaseUrl)
return self.init(modelContainer: try createModelContainer(databaseUrl: databaseUrl))
}
}
func isStorageEmpty() async -> Bool {
(try? self.modelContext.fetchCount(FetchDescriptor<SDConsumptionSession>())) ?? 0 == 0 // <-- Crash here!
}
func sessionsIn(interval: DateInterval) async throws -> [ConsumptionSession] {
let fetchDescriptor = FetchDescriptor(predicate: #Predicate<SDConsumptionSession> { sdSession in
if let startDate = sdSession.startTimeUtc {
return interval.start <= startDate && interval.end > startDate
} else {
return false
}
}, sortBy: self.sortDescriptor)
let consumptionSessions = try self.modelContext.fetch(fetchDescriptor) // <-- Crash here!
return consumptionSessions.map { ConsumptionSession(swiftDataSession: $0) }
}
func updateSessions(sessions: [ConsumptionSession]) async throws {
if #unavailable(iOS 18) {
// Price segments are duplicated if re-inserted so unfortunately we have to delete and reinsert sessions.
// On iOS 18, this is enforced by the #Unique macro on SDPriceSegment.
let sessionIds = Set(sessions.map(\.id))
try self.modelContext.delete(model: SDConsumptionSession.self, where: #Predicate<SDConsumptionSession> {
sessionIds.contains($0.id)
})
}
for session in sessions {
self.modelContext.insert(SDConsumptionSession(consumptionSession: session))
}
if self.modelContext.hasChanges {
try self.modelContext.save()
}
}
func deleteAllSessions() async {
if #available(iOS 18, *) {
try? self.modelContainer.erase()
} else {
self.modelContainer.deleteAllData()
}
}
}
The actor conforms to this protocol:
protocol ConsumptionSessionStorage {
func isStorageEmpty() async -> Bool
func hasCreditCardSessions() async -> Bool
func sessionsIn(interval: DateInterval) async throws -> [ConsumptionSession]
func updateSessions(sessions: [ConsumptionSession]) async throws
func deleteAllSessions() async
}
The crash is coming in from line 30 and 41, in other words, when trying to fetch data from the database. There doesn't seem to be any common trait for the crashes. They occur across iOS versions and device types.
Any idea what might cause this?
I'm working with SwiftData and SwiftUI and it's not clear to me if it is good practice to have a @ModelActor directly populate a SwiftUI view. For example when having to combine manual lab results and clinial results from HealthKit. The Clinical lab results are an async operation:
@ModelActor
actor LabResultsManager {
func fetchLabResultsWithHealthKit() async throws -> [LabResultDto] {
let manualEntries = try modelContext.fetch(FetchDescriptor<LabResult>())
let clinicalLabs = (try? await HealthKitService.getLabResults()) ?? []
return (manualEntries + clinicalLabs).sorted {
$0.date > $1.date
}.map {
return LabResultDto(from: $0)
}
}
}
struct ContentView: View {
@State private var labResults: [LabResultDto] = []
var body: some View {
List(labResults, id: \.id) { result in
VStack(alignment: .leading) {
Text(result.testName)
Text(result.date, style: .date)
}
}
.task {
do {
let labManager = LabResultsManager()
labResults = try await labManager.fetchLabResultsWithHealthKit()
} catch {
// Handle error
}
}
}
}
EDIT:
I have a few views that would want to use these labResults so I need an implementation that can be reused. Having to fetch and combine in each view will not be good practice. Can I pass a modelContext to a viewModel?
I've been seeing something that I find odd when using two SwiftData models where if I have one model (book, in this case) that has an optional array of another model (page, in this case), the optional array starts out as set to nil, but after about 20 seconds it updates to being an empty array.
I see it in Previews and after building.
Is this expected behavior? Should I just assume that if there is an optional array in my model it will eventually be initialized to an empty array?
Code is below.
import SwiftUI
import SwiftData
@Model
final class Book {
var title: String = "New Book"
@Relationship var pages: [Page]? = nil
init(title: String) {
self.title = title
}
}
@Model
final class Page {
var content: String = "Page Content"
var book: Book? = nil
init() {
}
}
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var books: [Book]
var body: some View {
NavigationSplitView {
List {
ForEach(books) { book in
NavigationLink {
Text("\(book.title)")
Text(book.pages?.debugDescription ?? "pages is nil")
} label: {
Text("\(book.title)")
Spacer()
Text("\(book.pages?.count.description ?? "pages is nil" )")
}
}
}
HStack {
Button("Clear Data") {
clearData()
}
Button("Add Book") {
addBook()
}
}
.navigationSplitViewColumnWidth(min: 180, ideal: 200)
} detail: {
Text("Select an item")
}
}
private func clearData() {
for book in books {
modelContext.delete(book)
}
try? modelContext.save()
}
private func addBook() {
let newBook = Book(title: "A New Book")
modelContext.insert(newBook)
}
}
@main
struct BookPageApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([Book.self, Page.self])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(sharedModelContainer)
}
}
#Preview {
ContentView()
.modelContainer(for: Book.self, inMemory: true)
}
Environment
visionOS 26
Xcode 26
Issue
I am experiencing crash when trying to access a [String] from a @Model data, after dismissing an immersiveSpace and opening a WindowGroup.
This crash only occurs when trying to access the [String] property of my Model. It works fine with other properties.
Thread 1: Fatal error: This backing data was detached from a context without resolving attribute faults: PersistentIdentifier(...)
Steps to Reproduce
Open WindowGroup
Dismiss window, open ImmersiveSpace
Dismiss ImmersiveSpace, reopen WindowGroup
Any guidance would be appreciated!
@main
struct MyApp: App {
var body: some Scene {
WindowGroup(id: "main") {
ContentView()
}
.modelContainer(for: [Item.self])
ImmersiveSpace(id: "immersive") {
ImmersiveView()
}
}
}
// In SwiftData model
@Model
class Item {
var title: String = "" // Accessing this property works fine
var tags: [String] = []
@storageRestrictions(accesses: _$backingData, initializes: _tags)
init(initialValue) {
_$backingData.setValue(forKey: \. tags, to: initialValue)
_tags =_ SwiftDataNoType()
}
get {
_$observationRegistrar.access(self, keyPath: \.tags)
**return self getValue(forkey: \.tags)** // Crashes here
}