Swift Data Fatal error: Duplicate keys of type 'AnyHashable' were found in a Dictionary

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, and when I query productions, it appears that it is still saved even though the app crashes.

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)
            }
        }
    }
}

Hi @The-Wolf

I just created a project using your code but since I don’t know what type of data you would be entering, I may not be able to replicate the problem you are experiencing.

By entering a random word in the Title and random words in the Character I am not seeing a crash like you are when saving but on the simulator I can see that the Xcode console is getting a bit angry with all kinds of weird diagnostics.

At the moment I don’t understand the relationship between Character and Production. They seem to be independent of each other. Does a Production have multiple characters? Is this related to theatre?

@Chris_Parker
Each Character references a Production. The app is crashing if you enter a title, create two Characters and select one character using the picker and then click save on iOS.

OK I got it to crash. Lemme think about this for a bit.

1 Like

Hi @The-Wolf

What I did was add an id attribute to each of the data model classes and there isn’t any more crashes as far as I have been able to test.

@Model
final class Character {
    var id = UUID()
    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 id = UUID()
    var name: String

    init(name: String) {
        self.name = name
    }
}

I’ve edited your content view so that you can see the data that has been saved and also added a swipe gesture so that you can delete line items to see the effect.

struct ContentView: View {
    @Environment(\.modelContext) private var context
    @State private var showingSheet = false
    @Query var characters: [Character]
    var body: some View {
        VStack {
            List {
                ForEach(characters) { character in
                    Text(character.production.name + " - " + character.name)
                        .swipeActions(edge: .trailing, allowsFullSwipe: true) {
                            Button("Delete") {
                                // Delete an item from the db
                                context.delete(character)
                            }
                            .tint(.red)
                        }
                }
            }

            Button("Add", systemImage: "plus") {
                showingSheet.toggle()
            }
        }
        .sheet(isPresented: $showingSheet) {
            AddProductionView()
        }
    }
}

Am I right in assuming that this is related to a theatre production in that you have a Production with a whole lot of Characters. I make that assumption based on the fact that in the AddProductionView you first add a title (which is the Production name) and then add Characters that appear in that Production. Is that a fair assumption?

So your data model is a one to one in that the Production name appears more than once in the Production model and each of the related character names appear in the Character model.

I hope that helps a bit.
Cheers