I am a beginner to programming, so I'd appreciate a beginner-level explanation, if at all possible.
I'm doing Paul Hudson's 100 days of SwiftUI course and we are supposed to make the keyPath
parameter for NSSortDescriptor
generic. This is inside of a custom view that can be used inside another view to build a list with a Core Data fetch request. I hope that made sense. I can't figure out how to make keyPath
generic, such that when I use this in another view I can pass in whatever keypath I want.
struct FilteredList<T: NSManagedObject, Content: View, V: Comparable>: View {
var fetchRequest: FetchRequest<T>
var singers: FetchedResults<T> { fetchRequest.wrappedValue }
var ascendingOrNot: Bool
var keyPath: KeyPath<T, V>
let content: (T) -> Content
var body: some View {
List(singers, id: \.self) { singer in
self.content(singer)
}
}
init(filterKey: String, filterValue: String, keyPath: KeyPath<T, V>, ascendingOrNot: Bool, @ViewBuilder content: @escaping (T) -> Content) {
self.keyPath = keyPath
self.ascendingOrNot = ascendingOrNot
fetchRequest = FetchRequest<T>(entity: T.entity(), sortDescriptors: [NSSortDescriptor(keyPath: keyPath, ascending: ascendingOrNot)], predicate: NSPredicate(format: "%K BEGINSWITH %@", filterKey, filterValue))
self.content = content
}
}
I tried adding the generic type V
and had it conform to Comparable
but when I tried to use this in another view, I got the following error: Generic parameter 'V' could not be inferred.
Here is how the view is being used in ContentView
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@State private var lastNameFilter = "A"
var body: some View {
VStack {
// list of matching singers
FilteredList(filterKey: "lastName", filterValue: lastNameFilter, keyPath: \Singer.lastName, ascendingOrNot: true) { singer in
Text("\(singer.wrappedLastName)")
}
Button("Add Examples") {
let taylor = Singer(context: self.moc)
taylor.firstName = "Taylor"
taylor.lastName = "Swift"
let ed = Singer(context: self.moc)
ed.firstName = "Ed"
ed.lastName = "Sheeran"
let adele = Singer(context: self.moc)
adele.firstName = "Adele"
adele.lastName = "Adkins"
try? self.moc.save()
}
Button("Show A") {
self.lastNameFilter = "A"
}
Button("Show S") {
self.lastNameFilter = "S"
}
}
}
}
Here is the Singer
entity
extension Singer {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Singer> {
return NSFetchRequest<Singer>(entityName: "Singer")
}
@NSManaged public var firstName: String?
@NSManaged public var lastName: String?
var wrappedFirstName: String {
firstName ?? "Unknown"
}
var wrappedLastName: String {
lastName ?? "Unknown"
}
}
Any assistance is very much appreciated.