Users have been reporting that the TestFlight version of my app (compiled with Xcode 26 Beta 6 17A5305f) is sometimes crashing on startup. Upon investigating their ips files, it looks like Core Data is locking up internally during its initialization, resulting in iOS killing my app. I have not recently changed my Core Data initialization logic, and it's unclear how I should proceed.
Is this a known issue? Any recommended workaround? I have attached the crash stack below.
Thanks!
crash_log.txt
iCloud & Data
RSS for tagLearn how to integrate your app with iCloud and data frameworks for effective data storage
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Here’s the situation:
• You’re downloading a huge list of data from iCloud.
• You’re saving it one by one (sequentially) into SwiftData.
• You don’t want the SwiftUI view to refresh until all the data is imported.
• After all the import is finished, SwiftUI should show the new data.
The Problem
If you insert into the same ModelContext that SwiftUI’s @Environment(.modelContext) is watching, each insert may cause SwiftUI to start reloading immediately.
That will make the UI feel slow, and glitchy, because SwiftUI will keep trying to re-render while you’re still importing.
How to achieve this in Swift Data ?
I'm trying to handle the serverRecordChanged return code you get in CKError when you have a conflict and your using the savePolicy of ifServerRecordUnchanged.
According to the CKError.Code.serverRecordChanged documentation, I should be receiving all three records that I need to do a 3-way merge. The problem is that the ancestorRecord (CKRecordChangedErrorAncestorRecordKey can also be used to look it up in the userInfo) doesn't actually contain a record. It only contains the record metadata.
Is there something I need to be doing to get the full ancestorRecord in the CKError?
If not is it possible to query iCloud for the ancestorRecord? Given that iCloud has the change history (as I understand it), then it is theoretically possible. I just don't know how to do it if it is possible.
Are 3-way merges even possible? The design of the serverRecordChanged looks like that is the intent, but I can't see how to do it with the data that CloudKit is providing.
Hi everyone,
I’m working on an offline-first iOS app using Core Data.
I have a question about safe future updates: in my project, I want to be able to add new optional fields to existing Entities or even completely new Entities in future versions — but nothing else (no renaming, deleting, or type changes).
Here’s how my current PersistenceController looks:
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "MyApp")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
print("Core Data failed to load store: \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
}
}
Do I need to explicitly set these properties to ensure lightweight migration works?
shouldMigrateStoreAutomatically = true
shouldInferMappingModelAutomatically = true
Or, according to the documentation, are they already true by default, so I can safely add optional fields and new Entities in future versions without breaking users’ existing data?
Thanks in advance for your guidance!
I have a SwiftData flashcard app which I am syncing with CloudKit using NSPersistentCloudKitContainer. While syncing itself is working perfectly, I have noticed a dramatic increase in the app size after enabling sync.
Specifically, without CloudKit, 15k flashcards results in the default.store file being about 4.5 MB. With CloudKit, default.store is about 67 MB. I have inspected the store and found that most of this increase is due to the ANSCKRECORDMETADATA table.
My question is, does implementing CloudKit normally cause this magnitude of increase in storage? If it doesn’t, is there something in my model, schema, implementation, etc. that could be causing it?
Below are two other posts describing a similar issue, but neither with a solution. I replied to the first one about a month ago. I then submitted this to Developer Technical Support, but was asked to post my question in the forums, so here it is.
Strange behavior with 100k+ records in NSPersistentCloudKitContainer
Huge increase in sqlite file size after adopting CloudKit
I'm experiencing the following error with my SwiftData container when running a build:
Code=134504 "Cannot use staged migration with an unknown model version."
Code Structure - Summary
I am using a versionedSchema to store multiple models in SwiftData. I started experiencing this issue when adding two new models in the newest Schema version. Starting from the current public version, V4.4.6, there are two migrations.
Migration Summary
The first migration is to V4.4.7. This is a lightweight migration removing one attribute from one of the models. This was tested and worked successfully.
The second migration is to V5.0.0. This is a custom migration adding two new models, and instantiating instances of the two new models based on data from instances of the existing models. In the initial testing of this version, no issues were observed.
Issue and Steps to Reproduce
Reproduction of issue: Starting from a fresh build of the publicly released V4.4.6, I run a new build that contains both Schema Versions (V4.4.7 and V5.0.0), and their associated migration stages. This builds successfully, and the container successfully migrates to V5.0.0. Checking the default.store file, all values appear to migrate and instantiate correctly.
The second step in reproduction of the issue is to simply stop running the build, and then rebuild, without any code changes. This fails to initialize the model container every time afterwards. Going back to the simulator after successive builds are stopped in Xcode, the app launches and accesses/modifies the model container as normal.
Supplementary Issue: I have been putting up with the same, persistent issue in the Xcode Preview Canvas of "Failed to Initialize Model Container" This is a 5 in 6 build issue, where builds will work at random. In the case of previews, I have cleared all data associated with all previews multiple times. The only difference being that the simulator is a 100% failure rate after the initial, successful initialization. I assume this is due to the different build structure of previews. Lastly, of note, the Xcode previews fail at the same line in instantiating the model container as the simulator does. From my research into this issue, people say that the Xcode preview is instantiating from elsewhere. I do have a separate model container set up specifically for canvas previews, but the error does not occur in that container, but rather the app's main container.
Possible Contributing Factors & Tested Facts
iOS: While I have experienced issues with SwiftData and the complier in iOS 26, I can rule that out as the issue here. This has been tested on simulators running iOS 18.6, 26.0.1, and 26.1, all encountering failures to initialize model container. While in iOS 18, subsequent builds after the successful migration did work, I did eventually encounter the same error and crash. In iOS 26.0.1 and 26.1, these errors come immediately on the second build.
Container Initialization for V4.4.6
do {
container = try ModelContainer(
for:
Job.self,
JobTask.self,
Day.self,
Charge.self,
Material.self,
Person.self,
TaskCategory.self,
Service.self,
migrationPlan: JobifyMigrationPlan.self
)
} catch {
fatalError("Failed to Initialize Model Container")
}
Versioned Schema Instance for V4.4.6 (V4.4.7 differs only by versionIdentifier)
static var versionIdentifier = Schema.Version(4, 4, 6)
static var models: [any PersistentModel.Type] {
[Job.self, JobTask.self, Day.self, Charge.self, Material.self, Person.self, TaskCategory.self, Service.self]
}
Container Initialization for V5.0.0
do {
let schema = Schema([Jobify.self,
JobTask.self,
Day.self,
Charge.self,
MaterialItem.self,
Person.self,
TaskCategory.self,
Service.self,
ServiceJob.self,
RecurerRule.self])
container = try ModelContainer(
for: schema, migrationPlan: JobifyMigrationPlan.self
)
} catch {
fatalError("Failed to Initialize Model Container")
}
Versioned Schema Instance for V5.0.0
static var versionIdentifier = Schema.Version(5, 0, 0)
static var models: [any PersistentModel.Type] {
[
JobifySchemaV500.Job.self,
JobifySchemaV500.JobTask.self,
JobifySchemaV500.Day.self,
JobifySchemaV500.Charge.self,
JobifySchemaV500.Material.self,
JobifySchemaV500.Person.self,
JobifySchemaV500.TaskCategory.self,
JobifySchemaV500.Service.self,
JobifySchemaV500.ServiceJob.self,
JobifySchemaV500.RecurerRule.self
]
}
Addressing Differences in Object Names
Type-aliasing: All my model types are type-aliased for simplification in view components. All types are aliased as 'JobifySchemeV446.<#Name#>' in V.4.4.6, and 'JobifySchemaV500.<#Name#>' in V5.0.0
Issues with iOS 26: My type-aliases dating back to iOS 17 overlapped with lower level objects in Swift, including 'Job' and 'Material'. These started to be an issue with initializing the model container when running in iOS 26. The type aliases have been renamed since, however the V4.4.6 build with the old names runs and builds perfectly fine in iOS 26
If there is any other code that may be relevant in determining where this error is occurring, I would be happy to add it. My current best theory is simply that I have mistakenly omitted code relevant to the SwiftData Migration.
Hello 👋,
I encounter the "This model instance was invalidated because its backing data could no longer be found the store" crash with SwiftData. Which from what I understood means I try to access a model after it has been removed from the store (makes sense).
I made a quick sample to reproduce/better understand because there some case(s) I can't figure it out.
Let's take a concrete example, we have Home model and a Home can have many Room(s).
// Sample code
@MainActor
let foo = Foo() // A single reference
let database = Database(modelContainer: sharedModelContainer) // A single reference
@MainActor
class Foo {
// Properties to explicilty keep reference of model(s) for the purpose of the POC
var _homes = [Home]()
var _rooms = [Room]()
func fetch() async {
let homes = await database.fetch().map {
sharedModelContainer.mainContext.model(for: $0) as! Home
}
print(ObjectIdentifier(homes[0]), homes[0].rooms?.map(\.id)) // This will crash here or not.
}
// Same version of a delete function with subtle changes.
// Depending on the one you use calling delete then fetch will result in a crash or not.
// Keep a reference to only homes == NO CRASH
func deleteV1() async {
self._homes = await database.fetch().map {
sharedModelContainer.mainContext.model(for: $0) as! Home
}
await database.delete()
}
// Keep a reference to only rooms == NO CRASH
func deleteV2() async {
self._rooms = await database.fetch().map {
sharedModelContainer.mainContext.model(for: $0) as! Home
}[0].rooms ?? []
await database.delete()
}
// Keep a reference to homes & rooms == CRASH 💥
func deleteV3() async {
self._homes = await database.fetch().map {
sharedModelContainer.mainContext.model(for: $0) as! Home
}
self._rooms = _homes[0].rooms ?? []
// or even only retain reference to rooms that have NOT been deleted 🤔 like here "id: 2" make it crash
// self._rooms = _homes[0].rooms?.filter { r in r.id == "2" } ?? []
await database.delete()
}
}
Calling deleteV() then fetch() will result in a crash or not depending on the scenario.
I guess I understand deleteV1, deleteV2. In those case an unsaved model is served by the model(for:) API and accessing properties later on will resolve correctly. The doc says: "The identified persistent model, if known to the context; otherwise, an unsaved model with its persistentModelID property set to persistentModelID."
But I'm not sure about deleteV3. It seems the ModelContext is kind of "aware" there is still cyclic reference between my models that are retained in my code so it will serve these instances instead when calling model(for:) API ? I see my home still have 4 rooms (instead of 2). So I then try to access rooms that are deleted and it crash. Why of that ? I mean why not returning home with two room like in deleteV1 ?
Because SwiftData heavily rely on CoreData may be I miss a very simple thing here. If someone read this and have a clue for me I would be extremely graceful.
PS:
If someone wants to run it on his machine here's some helpful code:
// Database
let sharedModelContainer: ModelContainer = {
let schema = Schema([
Home.self,
Room.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
debugPrint(modelConfiguration.url.absoluteString.replacing("%20", with: "\\ "))
return try! ModelContainer(for: schema, configurations: [modelConfiguration])
}()
extension Database {
static let shared = Database(modelContainer: sharedModelContainer)
}
@ModelActor
actor Database {
func insert() async {
let r1 = Room(id: "1", name: "R1")
let r2 = Room(id: "2", name: "R2")
let r3 = Room(id: "3", name: "R3")
let r4 = Room(id: "4", name: "R4")
let home = Home(id: "1", name: "My Home")
home.rooms = [r1, r2, r3, r4]
modelContext.insert(home)
try! modelContext.save()
}
func fetch() async -> [PersistentIdentifier] {
try! modelContext.fetchIdentifiers(FetchDescriptor<Home>())
}
@MainActor
func delete() async {
let mainContext = sharedModelContainer.mainContext
try! mainContext.delete(
model: Room.self,
where: #Predicate { r in
r.id == "1" || r.id == "4"
}
)
try! mainContext.save()
// 🤔 Calling fetch here seems to solve crash too, force home relationship to be rebuild correctly ?
// let _ = try! sharedModelContainer.mainContext.fetch(FetchDescriptor<Home>())
}
}
// Models
@Model
class Home: Identifiable {
@Attribute(.unique) public var id: String
var name: String
@Relationship(deleteRule: .cascade, inverse: \Room.home)
var rooms: [Room]?
init(id: String, name: String, rooms: [Room]? = nil) {
self.id = id
self.name = name
self.rooms = rooms
}
}
@Model
class Room: Identifiable {
@Attribute(.unique) public var id: String
var name: String
var home: Home?
init(id: String, name: String, home: Home? = nil) {
self.id = id
self.name = name
self.home = home
}
}
I am using SwiftData with CloudKit to synchronize data across multiple devices, and I have encountered an issue: occasionally, abnormal sync behavior occurs between two devices (it does not happen 100% of the time—only some users have reported this problem). It seems as if synchronization between the two devices completely stops; no matter what operations are performed on one end, the other end shows no response.
After investigating, I suspect the issue might be caused by both devices simultaneously modifying the same field, which could lead to CloudKit's logic being unable to handle such conflicts and causing the sync to stall. Are there any methods to avoid or resolve this situation?
Of course, I’m not entirely sure if this is the root cause. Has anyone encountered a similar issue?
I have a SwiftData application that is using CloudKit. If user is on new device. How can I check and fetch data, instead of just waiting for it happen on its own randomly?
For example, I have onboarding which I do not want user to go through again if they already have an active installation.
Seems like SwiftData is severely limited in pretty much every way, specially any useful CloudKit debugging or control functionality.
I have been working on an app for the past few months, and one issue that I have encountered a few times is an error where quick subsequent deletions cause issues with detached tasks that are triggered from some user actions.
Inside a Task.detached, I am building an isolated model context, querying for LineItems, then iterating over those items. The crash happens when accessing a Transaction property through a relationship.
var byTransactionId: [UUID: [LineItem]] {
return Dictionary(grouping: self) { item in
item.transaction?.id ?? UUID()
}
}
In this case, the transaction has been deleted, but the relationship existed when the fetch occurred, so the transaction value is non-nil. The crash occurs when accessing the id. This is the error.
SwiftData/BackingData.swift:1035: Fatal error: This model instance was invalidated because its backing data could no longer be found the store. PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(backing: SwiftData.PersistentIdentifier.PersistentIdentifierBacking.managedObjectID(0xb43fea2c4bc3b3f5 <x-coredata://A9EFB8E3-CB47-48B2-A7C4-6EEA25D27E2E/Transaction/p1756>)))
I see other posts about this error and am exploring some suggestions, but if anyone has any thoughts, they would be appreciated.
if it set com.apple.CoreData.ConcurrencyDebug 1 as launch arg the app always crashes and i cant proceed into the app. is there a way to only raise a warning for these issues so that i can go into the app and check every place in one session for coredata errors?
I have an iOS app (1Address) which allows users to share their address with family and friends using CloudKit Sharing.
Users share their address record (CKRecord) via a share link/url which when tapped allows the receiving user to accept the share and have a persistent view into the sharing user's address record (CKShare).
However, most users when they recieve a sharing link do not have the app installed yet, and so when a new receiving user taps the share link, it prompts them to download the app from the app store.
After the new user downloads the app from the app store and opens the app, my understanding is that the system (iOS) will/should then vend to my app the previously tapped cloudKitShareMetadata (or share url), however, this metadata is not being vended by the system. This forces the user to re-tap the share link and leads to some users thinking the app doesn't work or not completing the sharing / onboarding flow.
Is there a workaround or solve for this that doesn't require the user to tap the share link a second time?
In my scene delegate I am implementing:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {...}
And also
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {...}
And also:
func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {...}
And:
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {...}
Unfortunately, none of these are called or passed metadata on the initial app run after install. Only after the user goes back and taps a link again can they accept the share.
This documentation: https://developer.apple.com/documentation/cloudkit/ckshare says that adding the CKSharingSupported key to your app's Info.plist file allows the system to launch your app when a user taps or clicks a share URL, but it does not clarify what should happen if your app is being installed for the first time.
This seems to imply that the system is holding onto the share metadata and/or url, but for some reason it is not being vended to the app on first run.
Open to any ideas here for how to fix and I also filed feedback: FB20934189.
Hello Apple Team,
We’re building a CloudKit-enabled Core Data app and would like clarification on the behavior and performance characteristics of Binary Data attributes with “Allows External Storage” enabled when used with NSPersistentCloudKitContainer.
Initially, we tried storing image files manually on disk and only saving the metadata (file URLs, dimensions, etc.) in Core Data. While this approach reduced the size of the Core Data store, it introduced instability after app updates and broke sync between devices. We would prefer to use the official Apple-recommended method and have Core Data manage image storage and CloudKit syncing natively.
Specifically, we’d appreciate guidance on the following:
When a Binary Data attribute is marked as “Allows External Storage”, large image files are stored as separate files on device rather than inline in the SQLite store.
How effective is this mechanism in keeping the Core Data store size small on device?
Are there any recommended size thresholds or known limits for how many externally stored blobs can safely be managed this way?
How are these externally stored files handled during CloudKit sync?
Does each externally stored Binary Data attribute get mirrored to CloudKit as a CKAsset?
Does external storage reduce the sync payload size or network usage, or is the full binary data still uploaded/downloaded as part of the CKAsset?
Are there any bandwidth implications for users syncing via their private CloudKit database, versus developer costs in the public CloudKit database?
Is there any difference in CloudKit or Core Data behavior when a Binary Data attribute is managed this way versus manually storing image URLs and handling the file separately on disk?
Our goal is to store user-generated images efficiently and safely sync them via CloudKit, without incurring excessive local database bloat or CloudKit network overhead.
Any detailed guidance or internal performance considerations would be greatly appreciated.
Thank you,
Paul Barry
Founder & Lead Developer — Boat Buddy / Vessel Buddy iOS App
Archipelago Environmental Solutions Inc.
我正在使用 Core Data 开发一个 SwiftUI 项目。我的数据模型中有一个名为 AppleUser 的实体,具有以下属性:id (UUID)、name (String)、email (String)、password (String) 和 createdAt (Date)。所有属性都是非可选的。
我使用 Xcode 的自动生成创建了相应的 Core Data 类文件(AppleUser+CoreDataClass.swift 和 AppleUser+CoreDataProperties.swift)。我还有一个 PersistenceController,它使用模型名称 JobLinkModel 初始化 NSPersistentContainer。
当我尝试使用以下方法保存新的 AppleUser 对象时:
让用户 = AppleUser(上下文:viewContext)
user.id = UUID()
user.name = “用户 1”
user.email = “...”
user.password = “密码 1”
user.createdAt = Date()【电子邮件格式正确,但已替换为“...”出于隐私原因】
尝试?viewContext.save()
我在控制台中收到以下错误:核心数据保存失败:Foundation._GenericObjCError.nilError, [:]
用户快照: [“id”: ..., “name”: “User1”, “email”: “...”, “password”: “...”, “createdAt”: ...]
所有字段都有有效值,核心数据模型似乎正确。我还尝试过:
• 检查 NSPersistentContainer(name:) 中的模型名称是否与 .xcdatamodeld 文件 (JobLinkModel) 匹配
• 确保正确设置 AppleUser 实体类、模块和 Codegen(类定义、当前产品模块)
• 删除重复或旧的 AppleUser 类文件
• 清理 Xcode 构建文件夹并从模拟器中删除应用程序
• 对上下文使用 @Environment(.managedObjectContext)
尽管如此,在保存新的 AppleUser 对象时,我仍然会收到 _GenericObjCError.nilError。
我想了解:
为什么即使所有字段都不是零且正确分配,核心数据也无法保存?
这可能是由于一些残留的旧类文件引起的,还是我缺少设置中的其他内容?
我应该采取哪些步骤来确保 Core Data 正确识别 AppleUser 实体并允许保存?
任何帮助或指导将不胜感激。
I'm writing some tests to confirm the behavior of my app. White creating a model actor to delete objects I realized that ModelContext.model(for:) does return objects that are deleted. I was able to reproduces this with this minimal test case:
@Model class Activity {
init() {}
}
struct MyLibraryTests {
let modelContainer = try! ModelContainer(
for: Activity.self,
configurations: ModelConfiguration(
isStoredInMemoryOnly: true
)
)
init() throws {
let context = ModelContext(modelContainer)
context.insert(Activity())
try context.save()
}
@Test func modelForIdAfterDelete() async throws {
let context = ModelContext(modelContainer)
let id = try context.fetch(FetchDescriptor<Activity>()).first!.id
context.delete(context.model(for: id) as! Activity)
try context.save()
let result = context.model(for: id) as? Activity
#expect(result == nil) // Expectation failed: (result → MyLibrary.Activity) == nil
}
@Test func fetchDescriptorAfterDelete() async throws {
let context = ModelContext(modelContainer)
let id = try context.fetch(FetchDescriptor<Activity>()).first!.id
context.delete(context.model(for: id) as! Activity)
try context.save()
let result = try context.fetch(
FetchDescriptor<Activity>(predicate: #Predicate { $0.id == id })
).first
#expect(result == nil)
}
}
Here I create a new context, insert an model and save it.
The test modelForIdAfterDelete does fail, as result still contains the deleted object.
I also tried to check #expect(result!.isDeleted), but it is also false.
With the second test I use a FetchDescriptor to retrieve the object by ID and it correctly returns nil.
Shouldn't both methods use a consistent behavior?
I am working on a software where we want to add the feature to share the whole database with the other user. Database is iCloud combined with coredata. The other user(s) should be able to edit /delete and even create new objects in the share.
I did this with this code witch directly from sample code
let participants = try await ckConainer.fetchParticipants(matching: [lookupInfo], into: selectedStore)
for participant in participants {
participant.permission = .readWrite
participant.role = .privateUser
share.addParticipant(participant)
}
try await ckConainer.persistUpdatedShare(share, in: selectedStore)
the other user gets invited and I can see this in iCloud database that the other user is invited with status invited.
but the other user never gets a mail or something to accept and join the share. How does the other needs to accept the invitation ?
I have an app which uses key-value storage and will not sync data past a certain size -- meaning that device "A" will send the data to the cloud but device "B" will never receive the updated data. Device "B" will receive the NSUbiquitousKeyValueStoreDidChangeExternallyNotification that the KVS changed but the data is empty.
The data in in the KVS is comprised of 4 keys, each containing a value of NSData generated by NSKeyedArchiver. The NSData is comprised of property-list data types (e.g. numbers, strings, dates, etc.)
I've verified that the KVS meets the limits of:
A total of 1 MB per app, with a per-key limit of 1 MB
A per-key value size limit of 1 MB, and a maximum of 1024 keys
A maximum length for key strings is 64 bytes using UTF8 encoding
Also, the app has never received an NSUbiquitousKeyValueStoreQuotaViolationChange notification.
Of the 4 keys, 3 of them contain no more than 30 KB of data each. However, one of the keys can contain as much as 160 KB of data which will not sync to another device. Strangely, if I constrain the data to 100 KB it will work, however, that is not ideal as it is a fraction of the necessary data.
I don't see any errors in the debug log either.
Any suggestions on what to try next to get this working?
I'm running a project with these settings:
Default Actor Isolation: MainActor
Approachable Concurrency: Yes
Strict Concurrency Checking: Complete (this issue does not appear on the other two modes)
I receive a warning for this very simple use case. Can I actually fix anything about this or is this a case of Core Data not being entirely ready for this?
In reference to this, there was a workaround listed in the release notes of iOS 26 beta 5 (https://forums.swift.org/t/defaultisolation-mainactor-and-core-data-background-tasks/80569/22). Does this still apply as the only fix for this?
This is a simplified sample meant to run on a background context. The issue obviously goes away if this function would just run on the MainActor, then I can remove the perform block entirely.
class DataHandler {
func createItem() async {
let context = ...
await context.perform {
let newGame = Item(context: context)
/// Main actor-isolated property 'timestamp' can not be mutated from a Sendable closure
newGame.timestamp = Date.now
// ...
}
}
}
The complete use case would be more like this:
nonisolated
struct DataHandler {
@concurrent
func saveItem() async throws {
let context = await PersistenceController.shared.container.newBackgroundContext()
try await context.perform {
let newGame = Item(context: context)
newGame.timestamp = Date.now
try context.save()
}
}
}
Using SwiftData and this is the simplest example I could boil down:
@Model
final class Item {
var timestamp: Date
var tag: Tag?
init(timestamp: Date) {
self.timestamp = timestamp
}
}
@Model
final class Tag {
var timestamp: Date
init(timestamp: Date) {
self.timestamp = timestamp
}
}
Notice Tag has no reference to Item.
So if I create a bunch of items and set their Tag. Later on I add the ability to delete a Tag. Since I haven't added inverse relationship Item now references a tag that no longer exists so so I get these types of errors:
SwiftData/BackingData.swift:875: Fatal error: This model instance was invalidated because its backing data could no longer be found the store. PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-coredata://EEC1D410-F87E-4F1F-B82D-8F2153A0B23C/Tag/p1), implementation: SwiftData.PersistentIdentifierImplementation)
I think I understand now that I just need to add the item reference to Tag and SwiftData will nullify all Item references to that tag when a Tag is deleted.
But, the damage is already done. How can I iterate through all Items that referenced a deleted tag and set them to nil or to a placeholder Tag? Or how can I catch that error and fix it when it comes up?
The crash doesn't occur when loading an Item, only when accessing item.tag?.timestamp, in fact, item.tag?.id is still ok and doesn't crash since it doesn't have to load the backing data.
I've tried things like just looping through all items and setting tag to nil, but saving the model context fails because somewhere in there it still tries to validate the old value.
Thanks!
I've run into a strange issue.
If a sheet loads a view that has a SwiftData @Query, and there is an if statement in the view body, I get the following error when running an iOS targetted SwiftUI app under MacOS 26.1:
Set a .modelContext in view's environment to use Query
While the view actually ends up loading the correct data, before it does, it ends up re-creating the sqlite store (opening as /dev/null).
The strange thing is that this only happens if there is an if statement in the body. The statement need not ever evaluate true, but it causes the issue.
Here's an example. It's based on the default xcode new iOS project w/ SwiftData:
struct ContentView: View {
@State private var isShowingSheet = false
var body: some View {
Button(action: { isShowingSheet.toggle() }) {
Text("Show Sheet")
}
.sheet(isPresented: $isShowingSheet, onDismiss: didDismiss) {
VStack {
ContentSheetView()
}
}
}
func didDismiss() { }
}
struct ContentSheetView: View {
@Environment(\.modelContext) private var modelContext
@Query public var items: [Item]
@State var fault: Bool = false
var body: some View {
VStack {
if fault { Text("Fault!") }
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
List {
ForEach(items) { item in
Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))
}
}
}
}
private func addItem() {
withAnimation {
let newItem = Item(timestamp: Date())
modelContext.insert(newItem)
}
}
}
It requires some data to be added to trigger, but after adding it and dismissing the sheet, opening up the sheet with trigger the Set a .modelContext in view's environment to use Query. Flipping on -com.apple.CoreData.SQLDebug 1 will show it trying to recreate the database.
If you remove the if fault { Text("Fault!") } line, it goes away. It also doesn't appear to happen on iPhones or in the iPhone simulator.
Explicitly passing modelContext to the ContentSheetView like ContentSheetView().modelContext(modelContext) also seems to fix it.
Is this behavior expected?