For a book club app I'm writing In SwiftUI
, I have a NavigationSplitView
that has a selectable List
on its leading column. The first item on the list is "Book" and will take the user to a book's PDF if selected. Below it is a Button
with a plus icon that allows the user to add a reader of the book. Once a reader is added, their name will appear on a List
just below the plus Button
. If a user selects a reader, they'll be taken to their profile page.
The purpose of the whole thing is so that "Book" and the list of readers are integrated into one selectable List
, such that when one item is selected, it is highlighted and the previous selected item loses its highlight.
This is the code:
import SwiftUI
import SwiftData
enum ListItem: Hashable
{
case book
case reader(Reader)
}
struct ProjectView: View
{
private let project: Project
@Environment(\.modelContext) private var modelContext
@Query private var readers: [Reader]
@State private var alertPresented = false
@State private var userInput = ""
@State private var selectedListItem: ListItem?
init(project: Project)
{
self.project = project
}
var body: some View
{
NavigationSplitView
{
List(selection: self.$selectedListItem)
{
Text("Book").tag(ListItem.book)
Divider()
Button(action:
{
self.alertPresented = true
},
label:
{
Label("", systemImage: "plus")
})
.buttonStyle(PlainButtonStyle())
.alert("Reader Name:", isPresented: self.$alertPresented)
{
TextField("", text: self.$userInput)
Button("OK")
{
let reader = Reader(name: self.userInput)
self.modelContext.insert(reader)
self.project.readers?.append(reader)
self.selectedListItem = ListItem.reader(reader)
self.userInput = ""
}
.keyboardShortcut(.defaultAction)
Button("Cancel", role: .cancel) { }
}
ForEach (self.readers , id: \.self)
{
reader in Text(reader.name).tag(ListItem.reader(reader))
}
}
.onAppear
{
self.selectedListItem = ListItem.book
}
}
detail:
{
Text("Detail")
}
}
}
It was working fine until I added CloudKit
to my project. Now, on occasion, when I add a reader, I get a message:
Fatal error: Duplicate keys of type 'Reader' were found in a Dictionary. This usually means either that the type violates Hashable's requirements, or that member of such a dictionary were mutated after insertion.
Also, this List
will not work with CloudKit
and update any added readers on other devices (when other screens work fine with CloudKit). When I completely remove enum ListItem
and the "Book" item at the top, and simply display a list of readers using self.readers
and self.selectedReader
in place of self.selectedListItem
, then everything works fine. So I believe the issue is with using the enum and combining the "Book" item and the list of readers into one, but I'm not understanding why. Any help would be appreciated! For your reference, below are my Project
and Reader
model classes:
import Foundation
import SwiftData
@Model
class Project
{
var name: String = ""
@Relationship(deleteRule: .cascade, inverse: \Reader.projects) var readers : [Reader]?
init(name: String)
{
self.name = name
self.readers = []
}
}
import Foundation
import SwiftData
@Model
class Reader
{
var name: String = ""
var projects: [Project]?
init(name: String)
{
self.name = name
}
}
Was able to solve the problem from workingdog support Ukraine's suggestion to change ForEach(self.readers , id: \.self)
to ForEach(readers)
.