I get the following fatal error when the user clicks Save in AddProductionView.
Fatal error: Duplicate keys of type 'AnyHashable' were found in a Dictionary. This usually means either that the type violates Hashable's requirements, or that members of such a dictionary were mutated after insertion.
As far as I’m aware, SwiftData automatically makes its models conform to Hashable, so this shouldn’t be a problem.
I think it has something to do with the picker, but for the life of me I can’t see what.
This error occurs about 75% of the time when Save is clicked.
I'm using Xcode 16.2 and iPhone SE 2nd Gen. Any help would be greatly appreciated…
Here is my code:
import SwiftUI
import SwiftData
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.modelContainer(for: Character.self, isAutosaveEnabled: false)
}
}
}
@Model
final class Character {
var name: String
var production: Production
var myCharacter: Bool
init(name: String, production: Production, myCharacter: Bool = false) {
self.name = name
self.production = production
self.myCharacter = myCharacter
}
}
@Model
final class Production {
var name: String
init(name: String) {
self.name = name
}
}
struct ContentView: View {
@State private var showingSheet = false
var body: some View {
Button("Add", systemImage: "plus") {
showingSheet.toggle()
}
.sheet(isPresented: $showingSheet) {
AddProductionView()
}
}
}
struct AddProductionView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) var modelContext
@State var production = Production(name: "")
@Query var characters: [Character]
@State private var characterName: String = ""
@State private var selectedCharacter: Character?
var filteredCharacters: [Character] {
characters.filter { $0.production == production }
}
var body: some View {
NavigationStack {
Form {
Section("Details") {
TextField("Title", text: $production.name)
}
Section("Characters") {
List(filteredCharacters) { character in
Text(character.name)
}
HStack {
TextField("Character", text: $characterName)
Button("Add") {
let newCharacter = Character(name: characterName, production: production)
modelContext.insert(newCharacter)
characterName = ""
}
.disabled(characterName.isEmpty)
}
if !filteredCharacters.isEmpty {
Picker("Select your role", selection: $selectedCharacter) {
Text("Select")
.tag(nil as Character?)
ForEach(filteredCharacters) { character in
Text(character.name)
.tag(character as Character?)
}
}
.pickerStyle(.menu)
}
}
}
.toolbar {
Button("Save") { //Fatal error: Duplicate keys of type 'AnyHashable' were found in a Dictionary. This usually means either that the type violates Hashable's requirements, or that members of such a dictionary were mutated after insertion.
if let selectedCharacter = selectedCharacter {
selectedCharacter.myCharacter = true
}
modelContext.insert(production)
do {
try modelContext.save()
} catch {
print("Failed to save context: \(error)")
}
dismiss()
}
.disabled(production.name.isEmpty || selectedCharacter == nil)
}
}
}
}
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
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.
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?
Hey all,
This is my first app with Swift, and first app using CloudKit / iCloud - although I have launched other iOS app successfully.
When I created the app, I selected "none" for storage
my bundle identifier looks like this: io.mysite.appname
I have the iCloud capability added, with CloudKit checked, and the container also checked that looks like this: iCloud.io.mysite.appname
Push Notificaitons capability is also added, but there is no configuration.
I have tried automatically managed signing, as well as a manually created provisioning profile..
Every time I build the app onto my device - when I check it out in settings, icloud is not listed. When I go through iCloud into icloud drive, the app is also not listed.
I have cleaned the build many times, deleted and reinstalled the app on my phone many times. I am definitely logged into iCloud etc.
Obviously I have spent plenty of times trying to debug with various LLMs, but we all seem to be at a loss for what I'm missing or doing wrong.
Would love any tips or pointers I may be missing, thank you!
Topic:
App & System Services
SubTopic:
iCloud & Data
I have a ModelActor that creates a hierarchy of models and returns a PersistentIdentifier for the root. I'd like to do that in a transaction, but I don't know of a good method of getting that identifier if the models are created in a transaction.
For instance, an overly simple example:
func createItem(timestamp: Date) throws -> PersistentIdentifier {
try modelContext.transaction {
let item = Item(timestamp: timestamp)
modelContext.insert(item)
}
// how to return item.persistentModelID?
}
I can't return the item.persistentModelID from the transaction closure and even if I could, it will be a temporary ID until after the transaction is executed.
I can't create the Item outside the transaction and just have the transaction do an insert because swift will raise a data race error if you then try to return item.persistentModelID.
Is there any way to do this besides a modelContext.fetch* with separate unique identifiers?
We are trying to solve for the following condition with SwiftData + CloudKit:
Lots of data in CloudKit
Perform "app-reset" to clear data & App settings and start fresh.
Reset data models with try modelContext.delete(model:_) myModel.count() confirms local deletion (0 records); but iCloud Console shows expectedly slow process to delete.
Old CloudKit data is returning during the On Boarding process.
Questions:
• Would making a new iCloud Zone for each reset work around this, as the new zone would be empty? We're having trouble finding details about how to do this with SwiftData.
• Would CKSyncEngine have a benefit over the default SwiftData methods?
Open to hearing if anyone has experienced a similar challenge and how you worked around it!
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 ?
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!
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.
One question I often see on DevForums and in my day DTS job is if a Core Data object managed by NSPersistentCloudKitContainer can be shared with other iCloud users.
The answer is yes but you need to do it using CloudKit API directly because NSPersistentCloudKitContainer doesn’t support CloudKit shared database (CKContainer.sharedCloudDatabase) today.
Assuming you have a Core Data object, let’s say a document, that you’d like to collaborate with your colleagues:
You are the document owner and can use NSPersistentCloudKitContainer to fully manages the document and synchronize it across your devices.
You can grab a CloudKit record associated with your document from NSPersistentCloudKitContainer using record(for:) or recordID(for:), and share it to your colleagues using UICloudSharingController. See our Sharing CloudKit Data with Other iCloud Users - https://developer.apple.com/documentation/cloudkit/sharing_cloudkit_data_with_other_icloud_users sample for how to share a CloudKit record.
After accepting the sharing, your colleague, as a participant, can view or edit the shared document. The document resides in the participant’s CloudKit shared database and you have to manage it with your own code.
When your colleague edits and saves the shared document, the changes go to the owner’s private database, and eventually synchronize to NSPersistentCloudKitContainer on the owner side.
As you can see, you need to implement #2 and #3 with your own code because NSPersistentCloudKitContainer can’t manage the data in the participant's shared database. If you have any difficulty after going through the above sample code, you can contact Apple’s DTS for help.
I want to get to a point where I can use a small view with a query for my SwiftData model like this:
@Query
private var currentTrainingCycle: [TrainingCycle]
init(/*currentDate: Date*/) {
_currentTrainingCycle = Query(filter: #Predicate<TrainingCycle> {
$0.numberOfDays > 0
// $0.startDate < currentDate && currentDate < $0.endDate
}, sort: \.startDate)
}
The commented code is where I want to go. In this instance, it'd be created as a lazy var in a viewModel to have it stable (and not constantly re-creating the view). Since it was not working, I thought I could check the same view with a query that does not require any dynamic input. In this case, the numberOfDays never changes after instantiation.
But still, each time the app tries to create this view, the app becomes unresponsive, the CPU usage goes at 196%, memory goes way high and the device heats up quickly.
Am I holding it wrong? How can I have a dynamic predicate on a View in SwiftUI with SwiftData?
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()
}
}
}
Hi,
I'm getting a very odd error log in my SwiftData setup for an iOS app. It is implemented to support schema migration. When starting the app, it simply prints the following log twice (seems to be dependent on how many migration steps, I have two steps in my sample code):
CoreData: error: Attempting to retrieve an NSManagedObjectModel version checksum while the model is still editable. This may result in an unstable verison checksum. Add model to NSPersistentStoreCoordinator and try again.
(Yes there is a mistyped word "verison", this is exactly the log)
The code actually fully works. But I have neither CloudKit configured, nor is this app in Production yet. I'm still just developing.
Here is the setup and code to reproduce the issue.
Development mac version: macOS 15.5
XCode version: 16.4
iOS Simulator version: 18.5
Real iPhone version: 18.5
Project name: SwiftDataDebugApp
SwiftDataDebugApp.swift:
import SwiftUI
import SwiftData
@main
struct SwiftDataDebugApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Item.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false, allowsSave: true)
do {
return try ModelContainer(for: schema, migrationPlan: ModelMigraitonPlan.self, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(sharedModelContainer)
}
}
Item.swift:
import Foundation
import SwiftData
typealias Item = ModelSchemaV2_0_0.Item
enum ModelSchemaV1_0_0: VersionedSchema {
static var versionIdentifier = Schema.Version(1, 0, 0)
static var models: [any PersistentModel.Type] {
[Item.self]
}
@Model
final class Item {
var timestamp: Date
init(timestamp: Date) {
self.timestamp = timestamp
}
}
}
enum ModelSchemaV2_0_0: VersionedSchema {
static var versionIdentifier = Schema.Version(2, 0, 0)
static var models: [any PersistentModel.Type] {
[Item.self]
}
@Model
final class Item {
var timestamp: Date
var tags: [Tag] = []
init(timestamp: Date, tags: [Tag]) {
self.timestamp = timestamp
self.tags = tags
}
}
}
enum ModelMigraitonPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[ModelSchemaV1_0_0.self]
}
static var stages: [MigrationStage] {
[migrationV1_0_0toV2_0_0]
}
static let migrationV1_0_0toV2_0_0 = MigrationStage.custom(
fromVersion: ModelSchemaV1_0_0.self,
toVersion: ModelSchemaV2_0_0.self,
willMigrate: nil,
didMigrate: { context in
let items = try context.fetch(FetchDescriptor<ModelSchemaV2_0_0.Item>())
for item in items {
item.tags = Array(repeating: "abc", count: Int.random(in: 0...3)).map({ Tag(value: $0) })
}
try context.save()
}
)
}
Tag.swift:
import Foundation
struct Tag: Codable, Hashable, Comparable {
var value: String
init(value: String) {
self.value = value
}
static func < (lhs: Tag, rhs: Tag) -> Bool {
return lhs.value < rhs.value
}
static func == (lhs: Tag, rhs: Tag) -> Bool {
return lhs.value == rhs.value
}
func hash(into hasher: inout Hasher) {
hasher.combine(value)
}
}
ContentView.swift:
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
var body: some View {
VStack {
List {
ForEach(items) { item in
VStack(alignment: .leading) {
Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))
HStack {
ForEach(item.tags, id: \.hashValue) { tag in
Text("\(tag.value)")
}
}
}
}
.onDelete(perform: deleteItems)
}
Button("Add") {
addItem()
}
.padding(.top)
}
}
private func addItem() {
withAnimation {
let newItem = Item(timestamp: Date(), tags: [Tag(value: "Hi")])
modelContext.insert(newItem)
}
do {
try modelContext.save()
} catch {
print("Error saving add: \(error.localizedDescription)")
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
for index in offsets {
modelContext.delete(items[index])
}
}
do {
try modelContext.save()
} catch {
print("Error saving delete: \(error.localizedDescription)")
}
}
}
#Preview {
ContentView()
.modelContainer(for: Item.self, inMemory: true)
}
I hope someone can help, couldn't find anything related to this log at all.
According to my experiments SwiftData does not work with model attributes of primitive type UInt64. More precisely, it crashes in the getter of a UInt64 attribute invoked on an object fetched from the data store.
With Core Data persistent UInt64 attributes are not a problem. Does anyone know whether SwiftData will ever support UInt64?
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?
I work on an app that saves data to the Documents folder in the users iCloud Drive. This uses the iCloud -> iCloud Documents capability with a standard container.
We've noticed an issue where a user will delete the apps data by doing to Settings > {Name} > iCloud > Storage > App Name > select "delete data from iCloud", and then our app can no longer write to or create the Documents folder.
Once that happens, we get this error:
Error Domain=NSCocoaErrorDomain Code=513 "You don't have permission to save the file "Documents" in the folder "iCloud~your~bundle~identifier"." UserInfo={NSFilePath=/private/var/mobile/Library/Mobile Documents/iCloud~your~bundle~identifier/Documents, NSURL=file:///private/var/mobile/Library/Mobile%20Documents/iCloud~your~bundle~identifier/Documents, NSUnderlyingError=0x1102c7ea0 {Error Domain=NSPOSIXErrorDomain Code=13 "Permission denied"}}
This is reproducible using the sample project here https://developer.apple.com/documentation/uikit/synchronizing-documents-in-the-icloud-environment.
Steps to reproduce in that project:
Tap the plus sign in the top right corner to create a new document
Add a document name and tap "Save to Documents"
Go to Settings > {Name} > iCloud > Storage > SimpleiCloudDocument App Name > select "delete data from iCloud"
Reopen the app and repeat steps 1-2
Observe error on MainViewController+Document.swift:59
Deleting and reinstalling the app doesn't seem to help.
Topic:
App & System Services
SubTopic:
iCloud & Data
In a CloudKit private database, the Owner creates a custom zone and performs the following actions:
Creates CKRecord1 with CKShare1 and invites Participant1 to it.
Creates CKRecord2 with CKShare2 and invites Participant2 to it.
Creates CKRecordShared, which should be accessible to both Participant1 and Participant2.
How can I achieve step 3?
I observed that:
Setting a regular reference from CKRecord1 (or CKRecord2) to CKRecordShared does not automatically make CKRecordShared accessible to Participant1 (or Participant2).
CKRecordShared can only have one parent, so it cannot be directly linked via parent reference to both Participant1 and Participant2 at the same time.
One potential solution I see is to have the Owner create a separate CKShare for CKRecordShared and share it explicitly with each participant. However, this approach could lead to user errors, as it requires careful management of multiple shares for each participant.
Is there a better way to handle this scenario, ensuring that CKRecordShared is accessible to multiple participants without introducing unnecessary complexity or potential errors?
Hi,
I am creating (or trying to) my first app using SwiftData - and I have questions :-)
The main question I can't get my head wrapped around is the following:
Let's say I have the sample below...
@Model
class Person {
@Relationship(inverse:\Hat.owner) var hat:Hat
}
@Model
class Hat {
var owner:Person?
}
It looks like I am creating a strong reference cycle between the person and the hat objects? And in fact I am seeing these kinds of reference cycles when I look at the memory debugger.
Many code samples I have seen so far use this type of relationship declaration...
And I am wondering: Am I missing something?
Admittedly I don't find many discussions about memory leaks caused by SwiftData despite the syntax being used in many examples?
So what is the situation? Did Apple just miss to explain that the inverse: declaration causes memory leaks or is there some kind of magic that I should understand?
I'm using Swift Data for an app that requires iOS 18.
All of my models conform to a protocol that guarantees they have a 'serverID' String variable.
I wrote a function that would allow me to pass in a serverID String and have it fetch the model object that matched. Because I am lazy and don't like writing the same functions over and over, I used a Self reference so that all of my conforming models get this static function.
Imagine my model is called "WhatsNew". Here's some code defining the protocol and the fetching function.
protocol RemotelyFetchable: PersistentModel {
var serverID: String { get }
}
extension WhatsNew: RemotelyFetchable {}
extension RemotelyFetchable {
static func fetchOne(withServerID identifier: String, inContext modelContext: ModelContext) -> Self? {
var fetchDescriptor = FetchDescriptor<Self>()
fetchDescriptor.predicate = #Predicate<Self> { $0.serverID == identifier }
do {
let allModels = try modelContext.fetch(fetchDescriptor)
return allModels.first
} catch {
return nil
}
}
}
Worked great! Or so I thought...
I built this and happily ran a debug build in the Simulator and on devices for months while developing the initial version but when I went to go do a release build for TestFlight, that build reliably crashed on every device with a message like this:
SwiftData/DataUtilities.swift:65: Fatal error: Couldn't find \WhatsNew. on WhatsNew with fields [SwiftData.Schema.PropertyMetadata(name: "serverID", keypath: \WhatsNew., defaultValue: nil, metadata: Optional(Attribute - name: , options: [unique], valueType: Any, defaultValue: nil, hashModifier: nil)), SwiftData.Schema.PropertyMetadata(name: "title", keypath: \WhatsNew., defaultValue: nil, metadata: nil), SwiftData.Schema.PropertyMetadata(name: "bulletPoints", keypath: \WhatsNew.)>, defaultValue: nil, metadata: nil), SwiftData.Schema.PropertyMetadata(name: "dateDescription", keypath: \WhatsNew., defaultValue: nil, metadata: nil), SwiftData.Schema.PropertyMetadata(name: "readAt", keypath: \WhatsNew.)>, defaultValue: nil, metadata: nil)]
It seems (cannot confirm) that something in the release build optimization process is stripping out some metadata / something about these models that makes this predicate crash.
Tested on iOS 18.0 and 18.1 beta.
How can I resolve this? I have two dozen types that conform to this protocol. I could manually specialize this function for every type myself but... ugh.
I am attempting to migrate a cloudkit module that calls on manual cloudkit methods for fetching record zone changes, modifying records, etc to one that utilizes CKSyncEngine. I've got a basic implementation working with just a create method for one of my data models, however it seems like the sync engine keeps calling sync events on the same pending changes.
Here is my current flow:
The user will hit some button that lets them fill out a form to create a data model.
The user saves the form. This triggers a method that takes the resulting data model and queues it to the sync engine's state (engine.state.add(pendingRecordZoneChanges: pendingChanges)
I have my delegate method nextRecordZoneChangeBatch(_ context:...) implemented where it fetches the corresponding data model using the record ID and returns a batch containing the corresponding populated record from the data model.
I have the handleEvent(_ event:...) delegate method implemented where I handle both .fetchRecordZoneChanges and .sentRecordZoneChanges. I have set up .sentRecordZoneChanges to merge the server record into my local record (and persisted locally) so that the record change tags are the same.
After this last portion, it seems that the sync engine continues to keep pushing syncs/updates and I end up with numerous handleEvent(_ event:) calls that keep returning savedRecords (and occasionally failedRecordSaves).
Am I missing some step to remove the record from the changes after the sync engine recognizes that I have properly saved the record to the server?