Core Data

RSS for tag

Save your application’s permanent data for offline use, cache temporary data, and add undo functionality to your app on a single device using Core Data.

Posts under Core Data tag

149 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

ForEach and RandomAccessCollection
I'm trying to build a custom FetchRequest that I can use outside a View. I've built the following ObservableFetchRequest class based on this article: https://augmentedcode.io/2023/04/03/nsfetchedresultscontroller-wrapper-for-swiftui-view-models @Observable @MainActor class ObservableFetchRequest<Result: Storable>: NSObject, @preconcurrency NSFetchedResultsControllerDelegate { private let controller: NSFetchedResultsController<Result.E> private var results: [Result] = [] init(context: NSManagedObjectContext = .default, predicate: NSPredicate? = Result.E.defaultPredicate(), sortDescriptors: [NSSortDescriptor] = Result.E.sortDescripors) { guard let request = Result.E.fetchRequest() as? NSFetchRequest<Result.E> else { fatalError("Failed to create fetch request for \(Result.self)") } request.predicate = predicate request.sortDescriptors = sortDescriptors controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil) super.init() controller.delegate = self fetch() } private func fetch() { do { try controller.performFetch() refresh() } catch { fatalError("Failed to fetch results for \(Result.self)") } } private func refresh() { results = controller.fetchedObjects?.map { Result($0) } ?? [] } var predicate: NSPredicate? { get { controller.fetchRequest.predicate } set { controller.fetchRequest.predicate = newValue fetch() } } var sortDescriptors: [NSSortDescriptor] { get { controller.fetchRequest.sortDescriptors ?? [] } set { controller.fetchRequest.sortDescriptors = newValue.isEmpty ? nil : newValue fetch() } } internal func controllerDidChangeContent(_ controller: NSFetchedResultsController<any NSFetchRequestResult>) { refresh() } } Till this point, everything works fine. Then, I conformed my class to RandomAccessCollection, so I could use in a ForEach loop without having to access the results property. extension ObservableFetchRequest: @preconcurrency RandomAccessCollection, @preconcurrency MutableCollection { subscript(position: Index) -> Result { get { results[position] } set { results[position] = newValue } } public var endIndex: Index { results.endIndex } public var indices: Indices { results.indices } public var startIndex: Index { results.startIndex } public func distance(from start: Index, to end: Index) -> Int { results.distance(from: start, to: end) } public func index(_ i: Index, offsetBy distance: Int) -> Index { results.index(i, offsetBy: distance) } public func index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? { results.index(i, offsetBy: distance, limitedBy: limit) } public func index(after i: Index) -> Index { results.index(after: i) } public func index(before i: Index) -> Index { results.index(before: i) } public typealias Element = Result public typealias Index = Int } The issue is, when I update the ObservableFetchRequest predicate while searching, it causes a Index out of range error in the Collection subscript because the ForEach loop (or a List loop) access a old version of the array when the item property is optional. List(request, selection: $selection) { item in VStack(alignment: .leading) { Text(item.content) if let information = item.information { // here's the issue, if I leave this out, everything works Text(information) .font(.callout) .foregroundStyle(.secondary) } } .tag(item.id) .contextMenu { if Item.self is Client.Type { Button("Editar") { openWindow(ClientView(client: item as! Client), id: item.id!) } } } } Is it some RandomAccessCollection issue or a SwiftUI bug?
0
0
60
May ’25
XCode reverts CoreData's .xccurrentversion
I am experiencing an issue where XCode reverts .xccurrentversion file in my iOS app to the first version whenever xcodebuild is run or whenever XCode is started. This means I can build the app and run tests in XCode if I discard the reversion .xccurrentversion on XCode start. However, testing on CI is impossible because the version the tests rely on are reverted whenever xcodebuild is run. The commands I run to reproduce the issue ❯ git status Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: Path/.xccurrentversion no changes added to commit (use "git add" and/or "git commit -a") ❯ git checkout "Path/.xccurrentversion" Updated 1 path from the index ❯ git status nothing to commit, working tree clean ❯ xcodebuild \ -scheme Scheme \ -configuration Configuration \ -sdk iphonesimulator \ -destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=latest' \ -skipPackagePluginValidation \ -skipMacroValidation \ test > /dev/null # test fails because model version is reverted ❯ git status HEAD detached at pull/249/merge Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: Path/.xccurrentversion no changes added to commit (use "git add" and/or "git commit -a") I have experienced such issue in 16.3 (16E140) and 16.2 (16C5032a). Similar issues/solutions I have found online are the following. But they are either not relevant or do not work in my case. https://stackoverflow.com/questions/17631587/xcode-modifies-current-coredata-model-version-at-every-launch https://github.com/CocoaPods/Xcodeproj/issues/81 Is anyone aware of any solution? Is there a recommended way I can run diagnostics on XCode and file a feedback?
11
0
118
May ’25
Core Data complaining about store being opened without persistent history tracking... but I don't think that it has been
Since running on iOS 14b1, I'm getting this in my log (I have Core Data logging enabled): error: Store opened without NSPersistentHistoryTrackingKey but previously had been opened with NSPersistentHistoryTrackingKey - Forcing into Read Only mode store at 'file:///private/var/mobile/Containers/Shared/AppGroup/415B75A6-92C3-45FE-BE13-7D48D35909AF/StoreFile.sqlite' As far as I can tell, it's impossible to open my store without that key set - it's in the init() of my NSPersistentContainer subclass, before anyone calls it to load stores. Any ideas?
2
0
1.1k
May ’25
CoreData w/ Private and Shared Configurations
I have a CoreData model with two configuration - but several problems. Notably the viewContext only shows data from the .private configuration. Here is the setup: The private configuration holds entities, for example, User and Course and the shared one holds entities, for example, Player and League. I setup the NSPersistentStoreDescriptions to use the same container but with a databaseScope of .private/.shared and with the configuration of "Private"/"Shared". loadPersistentStores() does not report an error. If I try container.initializeCloudKitSchema() only the .private configuration produces CKRecord types. If I create a companion app using one configuration (w/ all entities) the schema initialization creates all CKRecord types AND I can populate some data in the .private and a created CKShare. I see that data in the CloudKit dashboard. If I axe the companion app and run the real thing w/ two configurations, the viewContext only has the .private data. Why? If when querying history I use NSPersistentHistoryTransaction.fetchRequest I get a nil return when using two configurations (but non-nil when using one).
0
0
43
Apr ’25
Cannot Find UI to Add Core Data Database Indexes in Xcode 16.2
Hi everyone, I'm trying to add standard, non-unique database indexes to my Core Data entities for performance optimization (e.g., indexing Date or String attributes used in predicates and sort descriptors). I'm using Xcode 16.2 on macOS Sequoia 15.1. My problem is that I cannot find the expected UI element in the Core Data model editor (.xcdatamodeld) to configure these database indexes. What I Understand / Expect: I know the old "Indexed" checkbox on the Attribute Inspector is deprecated/gone. My understanding from recent documentation and tutorials is that database indexing (separate from Spotlight indexing) should be configured in the Entity Inspector (when the Entity itself is selected), within a section titled "Indexes" (usually located below "Constraints"). This "Indexes" section should allow adding individual or compound indexes that translate to SQL CREATE INDEX commands, distinct from uniqueness constraints. What I'm Experiencing: When I select an Entity in the model editor, the "Indexes" section is completely missing from the Data Model Inspector pane on the right. I see sections for Name, Class, Constraints, Spotlight, User Info, Versioning, etc., but no "Indexes" section appears between Constraints and Spotlight (or anywhere else). Troubleshooting Steps Taken: Verified Selection: I have confirmed I am selecting the Entity itself in the left-hand list, not an individual Attribute. Ruled out Spotlight Indexing: I understand the difference between database indexing (for internal query performance) and the "Index in Spotlight" checkbox/Core Spotlight framework (for system search). I specifically need the former. Basic Xcode Troubleshooting: I have tried restarting Xcode, cleaning the build folder (Shift+Command+K), and deleting the project's Derived Data. The "Indexes" section remains missing. Checked File Placement/Target Membership: Confirmed the .xcdatamodeld file is correctly included in the target. Its location in the project navigator doesn't seem relevant. Checked Model Versioning: Ensured the correct model version is set as "Current" in the File Inspector. Ruled out Other Features: Confirmed that Fetch Requests, Fetched Properties, and User Info keys are not the mechanisms for defining database indexes. Confirmed Not Project-Specific: I created a brand new, template-generated iOS App project with "Use Core Data" checked. In this new project, when selecting the default "Item" entity, the "Indexes" section is also missing from the Entity Inspector. This strongly suggests the issue is with my Xcode environment/version itself, not my specific project's setup. Considered Programmatic/Manual: I understand Core Data expects schema definitions (including indexes) declaratively in the model file. While manual XML editing of the contents file works (adding ... within the tag), this is not the desired or intended workflow via the standard tools. My Questions: What is the correct, current procedure for defining non-unique Core Data database indexes using the Xcode UI in Xcode 16.2? Has the location or method for configuring database indexes changed in this version of Xcode? If so, where is it now? Is the absence of the "Indexes" section in the Entity Inspector a known issue or intentional change for this Xcode version? If the standard UI method is unavailable, what is the officially recommended approach (other than manual XML editing)? I've reviewed the documentation ("Configuring Entities", "Configuring Attributes") and while screenshots show the inspectors, they don't definitively show the "Indexes" section within the Entity Inspector, sometimes focusing on attributes or potentially being cropped. Any clarification or guidance would be greatly appreciated!
0
0
33
Apr ’25
Why Must All Attributes in a Composite Type Be Optional?
I recently encountered an issue involving Core Data’s new Composite Attributes feature and thought I would share my experience, as well as seek clarification. I created a composite type where all attributes were mandatory, except for one. Subsequently, I added an attribute to an entity and set its type to that composite type. Upon running the app, the console output the following error: CoreData: error: CoreData: error: Row (pk = 85) for entity ‘(EntityName)’ is missing mandatory text data for property ‘(propertyName)’ The way I resolved this was by removing the composite type attribute from the entity, after which the error no longer appeared. I also observed that in another entity, where a different composite type is used, all the attributes were optional — and no error occurred. This raises the question: why must all attributes in a composite type be optional? Furthermore, why does Xcode not inform the developer of this requirement? I have reviewed both the documentation and the WWDC23 “What’s New in Core Data” session, but neither mentions that having non-optional attributes within a composite type will cause such errors and lead to unpredictable application behaviour. Additionally, this issue remains unresolved in another area I raised previously in this topic: Composite Attributes feature requires tvOS deployment target 17.0 or later Composite Attributes feature requires watchOS deployment target 10.0 or later However, I do not have a tvOS or watchOS target, nor do I intend to add one. Could someone from Apple, or anyone with more experience, please clarify why all attributes within a composite type must be optional? And could it be possible for Xcode to flag this at compile time, rather than failing at runtime? Thank you in advance.
0
0
37
Apr ’25
"Failed to set up CloudKit integration" in TestFlight build
I'm building a macOS + iOS SwiftUI app using Xcode 14.1b3 on a Mac running macOS 13.b11. The app uses Core Data + CloudKit. With development builds, CloudKit integration works on the Mac app and the iOS app. Existing records are fetched from iCloud, and new records are uploaded to iCloud. Everybody's happy. With TestFlight builds, the iOS app has no problems. But CloudKit integration isn't working in the Mac app at all. No existing records are fetched, no new records are uploaded. In the Console, I see this message: error: CoreData+CloudKit: Failed to set up CloudKit integration for store: <NSSQLCore: 0x1324079e0> (URL: <local file url>) Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named com.apple.cloudd was invalidated: failed at lookup with error 159 - Sandbox restriction." UserInfo={NSDebugDescription=The connection to service named com.apple.cloudd was invalidated: failed at lookup with error 159 - Sandbox restriction.} I thought it might be that I was missing the com.apple.security.network.client entitlement, but adding that didn't help. Any suggestions what I might be missing? (It's my first sandboxed Mac app, so it might be really obvious to anyone but me.)
4
1
3.3k
Apr ’25
Core Data and Swift 6 concurrency: returning an NSManagedObject
We're in the process of migrating our app to the Swift 6 language mode. I have hit a road block that I cannot wrap my head around, and it concerns Core Data and how we work with NSManagedObject instances. Greatly simplied, our Core Data stack looks like this: class CoreDataStack { private let persistentContainer: NSPersistentContainer var viewContext: NSManagedObjectContext { persistentContainer.viewContext } } For accessing the database, we provide Controller classes such as e.g. class PersonController { private let coreDataStack: CoreDataStack func fetchPerson(byName name: String) async throws -> Person? { try await coreDataStack.viewContext.perform { let fetchRequest = NSFetchRequest<Person>() fetchRequest.predicate = NSPredicate(format: "name == %@", name) return try fetchRequest.execute().first } } } Our view controllers use such controllers to fetch objects and populate their UI with it: class MyViewController: UIViewController { private let chatController: PersonController private let ageLabel: UILabel func populateAgeLabel(name: String) { Task { let person = try? await chatController.fetchPerson(byName: name) ageLabel.text = "\(person?.age ?? 0)" } } } This works very well, and there are no concurrency problems since the managed objects are fetched from the view context and accessed only in the main thread. When turning on Swift 6 language mode, however, the compiler complains about the line calling the controller method: Non-sendable result type 'Person?' cannot be sent from nonisolated context in call to instance method 'fetchPerson(byName:)' Ok, fair enough, NSManagedObject is not Sendable. No biggie, just add @MainActor to the controller method, so it can be called from view controllers which are also main actor. However, now the compiler shows the same error at the controller method calling viewContext.perform: Non-sendable result type 'Person?' cannot be sent from nonisolated context in call to instance method 'perform(schedule:_:)' And now I'm stumped. Does this mean NSManageObject instances cannot even be returned from calls to NSManagedObjectContext.perform? Ever? Even though in this case, @MainActor matches the context's actor isolation (since it's the view context)? Of course, in this simple example the controller method could just return the age directly, and more complex scenarios could return Sendable data structures that are instantiated inside the perform closure. But is that really the only legal solution? That would mean a huge refactoring challenge for our app, since we use NSManageObject instances fetched from the view context everywhere. That's what the view context is for, right? tl;dr: is it possible to return NSManagedObject instances fetched from the view context with Swift 6 strict concurrency enabled, and if so how?
0
0
51
Apr ’25
Core Data transformable attribute problem in Xcode16
Hi everyone, Have anybody faced with Core Data issues, trying to migrate the project to Xcode16 beta 4? We are using transformableAttributeType in some entities, with attributeValueClassName = "[String]" and valueTransformerName = "NSSecureUnarchiveFromData". It is working just fine for years, but now I am trying to run the project from Xcode16 and have 2 issues: in Xcode logs I see warning and error: CoreData: fault: Declared Objective-C type "[String]" for attribute named alertBarChannels is not valid CoreData: Declared Objective-C type "[String]" for attribute named alertBarChannels is not valid periodically the app crashes when we are assigning value to this attribute, with error: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFConstantString characterAtIndex:]: Range or index out of bounds' Once again, in Xcode 15 it works fine, and it was working for years. Cannot find any information about what was changed in the framework... Thank you in advance for any information, which could clarify what is going on.
15
14
3.9k
Apr ’25
CloudKit is not synchronizing with coredata for relationships
In core-data I have a contact and location entity. I have one-to-many relationship from contact to locations and one-to-one from location to contact. I create contact in a seperate view and save it. Later I create a location, fetch the created contact, and save it while specifying the relationship between location and contact contact and test if it actually did it and it works. viewContext.perform { do { // Set relationship using the generated accessor method currentContact.addToLocations(location) try viewContext.save() print("Saved successfully. Locations count:", currentContact.locations?.count ?? 0) if let locs = currentContact.locations { print("📍 Contact has \(locs.count) locations.") for loc in locs { print("➡️ Location: \(String(describing: (loc as AnyObject).locationName ?? "Unnamed"))") } } } catch { print("Failed to save location: \(error.localizedDescription)") } } In my NSManagedObject class properties I have this : for Contact: @NSManaged public var locations: NSSet? for Location: @NSManaged public var contact: Contact? in my persistenceController I have: for desc in [publicStore, privateStore] { desc.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) desc.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) desc.setOption(true as NSNumber, forKey: NSMigratePersistentStoresAutomaticallyOption) desc.setOption(true as NSNumber, forKey: NSInferMappingModelAutomaticallyOption) desc.setOption(true as NSNumber, forKey: "CKSyncCoreDataDebug") // Optional: Debug sync // Add these critical options for relationship sync desc.setOption(true as NSNumber, forKey: "NSPersistentStoreCloudKitEnforceRecordExistsKey") desc.setOption(true as NSNumber, forKey: "NSPersistentStoreCloudKitMaintainReferentialIntegrityKey") // Add this specific option to force schema update desc.setOption(true as NSNumber, forKey: "NSPersistentStoreRemoteStoreUseCloudKitSchemaKey") } When synchronization happens on CloudKit side, it creates CKRecords: CD_Contact and CD_Location. However for CD_Location it creates the relationship CD_contact as a string and references the CD_Contact. This I thought should have come as REFERENCE On the CD_Contact there is no CD_locations field at all. I do see the relationships being printed on coredata side but it does not come as REFERENCE on cloudkit. Spent over a day on this. Is this normal, what am I doing wrong here? Can someone advise?
0
0
50
Apr ’25
How to Resolve Core Data Composite Attributes Errors in Xcode?
How do I resolve these errors reported in Xcode about using Core Data Composite Attributes feature? Composite Attributes feature requires tvOS deployment target 17.0 or later Composite Attributes feature requires watchOS deployment target 10.0 or later My app is a Cocoa app and targets macOS 14.0+. I do not have an iOS, iPadOS, tvOS nor watchOS targets. So, I don't understand why I am getting these errors. My Xcode project format is set to Xcode 16.0. I am on macOS Sequoia 15.2 (24C101) and using Xcode Version 16.2 (16C5032a). If you create a new Xcode project and choose macOS > App and Storage > Core Data and then Add Composite Type and set an attribute name and type, you will get the errors.
3
0
475
Apr ’25
Sync an interactive widget's Core Data store with the main app (and iCloud)
Hi everyone! I have an app on the App Store that uses Core Data as its data store. (It's called Count on Me: Tally Counter. Feel free to check it out.) One of the app's core feature is an interactive widget with a simple button. When the button is tapped, it's supposed to update the entity in the store. My requirement is that the changes are then reflected with minimal latency in the main app and – ideally – also on other devices of the same iCloud user. And vice-versa: When an entity is updated in the app (or on another device where the same iCloud user is logged in), the widget that shows this entity should also refresh to reflect the changes. I have read multiple articles, downloaded sample projects, searched Stackoverflow and the Apple developer forums, and tried to squeeze a solution out of AI, but couldn't figure out how to make this work reliably. So I tried to reduce the core problem to a minimal example project. It has two issues that I cannot resolve: When I update an entity in the app, the widget is immediately updated as intended (due to a call to WidgetCenter's reloadAllTimelines method). However, when I update the same entity from the interactive widget using the same app intent, the changes are not reflected in the main app. For the widget and the app to use the same local data store, I need to enable App Groups in both targets and set a custom location for the store within the shared app group. So I specify a custom URL for the NSPersistentStoreDescription when setting up the Core Data stack. The moment I do this, iCloud sync breaks. Issue no. 1 is far more important to me as I haven't officially enabled iCloud sync yet in my real app that's already on the App Store. But it would be wonderful to resolve issue no. 2 as well. Surely, there must be a way to synchronize changes to the source of truth triggered by interactive widget with other devices of the same iCloud user. Otherwise, the feature to talk to the main app and the feature to synchronize with iCloud would be mutually exclusive. Some other developers I talked to have suggested that the widget should only communicate proposed changes to the main app and once the main app is opened, it processes these changes and writes them to the NSPersistentCloudKitContainer which then synchronizes across devices. This is not an option for me as it would result in a stale state and potential data conflicts with different devices. For example, when a user has the same widget on their iPhone and their iPad, taps a button on the iPhone widget, that change would not be reflected on the iPad widget until the user decides to open the app on the iPhone. At the same time, the user could tap the button multiple times on their iPad widget, resulting in a conflicting state on both devices. Thus, this approach is not a viable solution. An answer to this question will be greatly appreciated. The whole code including the setup of the Core Data stack is included in the repository reference above. Thank you!
4
0
274
Apr ’25
Unable to load data from Core Data in SwiftUI app (very rare)
Hey, We're loading data from Core Data, and for some reason an error is thrown. This is happening extremely rarely and we haven't been able to reproduce it. The error thrown has the following description: Åtgärden kunde inte slutföras. (ScreenGenieCore.EnrolledView.(unknown context at $10087af4c).EnrolledError fel 0.) It is occurring in an app written in SwiftUI when the user taps a button. The managed object context is initiated in app init and provided to the view using the @environment modifier. So the viewContext should always exist. Still it throws an error saying unknown context .... Any guidance or possible things to investigate would be much appreciated.
1
0
47
Apr ’25
How to provide visual feedback about iCloud sync status when the user reinstalls an app?
It takes a few seconds, sometimes a few minutes for records to be downloaded back from CloudKit when the user reinstalls the app, which leads users to thinking their data was lost. I would like to know if there’s any way to provide a visual feedback about the current CloudKit sync status so I can let users know their data is being in fact downloaded back to their devices.
2
0
185
Mar ’25
EXC_BAD_INSTRUCTION
public static func fetch(in context: NSManagedObjectContext, configurationBlock: (NSFetchRequest) -&amp;gt; () = { _ in }) -&amp;gt; [Self] { let request = NSFetchRequest(entityName: Self.entityName) configurationBlock(request) return try! context.fetch(request) } context.fetch(request), 'fetch' function has error. Thread 24: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
7
0
659
Mar ’25
Swift UI on iOS 14 not assigning new object to @State property
On iOS 13 I used to use optional @State properties to adapt views. In my case, the presented view would either create a new object (an assignment) if the state that is passed into it is nil, or edit the assignment if an assignment was passed in. This would be done in the action block of a Button and it worked beautifully. On iOS 14 / Xcode 12 this no longer seems to work. Given the following code which creates a new assignment and passes it into the editor view when the user taps a "New Assignment" button, the value of assignment remains nil. Is anyone else experiencing similar behaviour? struct ContentView: View { &#9;&#9;@Environment(\.managedObjectContext) var context &#9;&#9;@State var assignmentEditorIsPresented = false &#9;&#9;@State var assignment: Assignment? = nil &#9;&#9;var Body: some View { &#9;&#9;&#9;&#9;[...] &#9;&#9;&#9;&#9;Button("New Assignment", action: { &#9;&#9;&#9;&#9;&#9;&#9;self.assignment = Assignment(context: context) &#9;&#9;&#9;&#9;&#9;&#9;self.assignmentEditorIsPresented = true &#9;&#9;&#9;&#9;}) &#9;&#9;&#9;&#9;.sheet(isPresented: assignmentEditorIsPresented) { &#9;&#9;&#9;&#9;&#9;&#9;[...] &#9;&#9;&#9;&#9;} &#9;&#9;} } What's even weirder is that I tried adding a random piece of state, an Int, to this view and modifying it right before the assignment state (between lines 9 and 10) and it didn't change either.
21
4
13k
Mar ’25
dual predicate search using CoreData
I have a very simple CoreData model that has 1 entity and 2 attributes. This code works fine: .onChange(of: searchText) { _, text in evnts.nsPredicate = text.isEmpty ? nil :NSPredicate(format: "eventName CONTAINS %@ " , text ) but I'd like to also search with the same text string for my second attribute (which is a Date). I believe an OR is appropriate for two conditions (find either one). See attempted code below: evnts.nsPredicate = text.isEmpty ? nil : NSPredicate(format: "(eventName CONTAINS %@) OR (dueDate CONTAINS %i) " , text ) This crashes immediately %@ does the same. Is there a way to accomplish this? How is SwiftUI not an option below?
6
0
310
Mar ’25
Crash during batch deletion merge when positive fractional decimals are stored and used in a derived attribute
I am experiencing a crash when performing a batch delete and merging changes on a Core Data store that uses NSPersistentCloudKitContainer. The crash appears to be triggered when positive fractional Decimal values are stored in a TransactionSplit entity (those values are aggregated via a derived attribute in the AccountTransaction entity). If I store whole numbers or negative fractional decimals, deletion seems to work correctly. I suspect that the issue is related to the internal representation of positive fractional decimals in conjunction with a derived attribute. Data Model Setup: Account (1:N relationship → AccountTransaction) AccountTransaction (1:N relationship → TransactionSplit), which contains a derived attribute (e.g., “splits.amount.@sum”) that computes the sum over the “amount” attribute on its related TransactionSplit objects. TransactionSplit, which contains a stored Decimal attribute named “amount” (of type Decimal/NSDecimalNumber). Steps to Reproduce: Insert sample data where each TransactionSplit’s “amount” is set to a positive fractional value (e.g., 1000.01), by using code similar to: func createSampleData() { // Execute all creation on the context’s queue. let checkingAccount = Account(context: context) checkingAccount.id = UUID() checkingAccount.name = "Main Checking" let randomTransactionCount = 1000 for _ in 0..<randomTransactionCount { let transaction = AccountTransaction(context: context) transaction.id = UUID() transaction.account = checkingAccount let randomValue = Double.random(in: 5...5000) let decimalValue = NSDecimalNumber(value: randomValue) let split1 = TransactionSplit(context: context) split1.id = UUID() split1.amount = decimalValue split1.transaction = transaction let split2 = TransactionSplit(context: context) split2.id = UUID() split2.amount = decimalValue split2.transaction = transaction } save() } The AccountTransaction’s derived attribute automatically aggregates the sum of its related TransactionSplit amounts. Perform a batch deletion using NSBatchDeleteRequest (with resultType set to .resultTypeObjectIDs) on your entities and merge the changes back into your main context: private func delete(_ fetchRequest: NSFetchRequest<NSFetchRequestResult>) { let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) batchDeleteRequest.resultType = .resultTypeObjectIDs // ⚠️ When performing a batch delete we need to make sure we read the result back // then merge all the changes from that result back into our live view context // so that the two stay in sync. if let delete = try? context.execute(batchDeleteRequest) as? NSBatchDeleteResult { let changes = [NSDeletedObjectsKey: delete.result as? [NSManagedObjectID] ?? []] NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [context]) } } Save the context after deletion.
4
0
465
Mar ’25