We’ve arrived at day 59 of the 100 Days of SwiftUI! Yesterday, we had a deep dive into Core Data and that continues today, as we face three challenges to test the foundations of our Core Data knowledge. All three of these tasks require you to modify the FilteredList
view we made. Let’s dive in!
Core Data SwiftUI challenge #1
The first challenge is to make it accept a string parameter that controls which predicate is applied.
//
// FilteredList.swift
// CoreDataProject
import CoreData
import SwiftUI
enum FilterType: String {
case beginsWith = "BEGINSWITH"
case contains = "CONTAINS[c]"
}
struct FilteredList<T: NSManagedObject, Content: View>: View {
@FetchRequest var fetchRequest: FetchedResults<T>
// this is our content closure; we'll call this once for each item in the list
let content: (T) -> Content
var body: some View {
List(fetchRequest, id: \.self) { singer in
self.content(singer)
}
}
init(type: FilterType = .contains, filterKey: String, filterValue: String, sortDescriptors: [SortDescriptor<T>] = [], @ViewBuilder content: @escaping (T) -> Content) {
_fetchRequest = FetchRequest<T>(sortDescriptors: sortDescriptors, predicate: NSPredicate(format: "%K \(type.rawValue) %@", filterKey, filterValue))
self.content = content
}
}
Core Data SwiftUI challenge #2
The second challenge is to modify the predicate string parameter to be an enum such as .beginsWith
, then make that enum get resolved to a string inside the initializer.
//
// ContentView.swift
// CoreDataProject
import CoreData
import SwiftUI
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@State private var lastNameFilter = "A"
@State private var filterType = FilterType.contains
@State private var sortDescriptors = [SortDescriptor<Singer>]()
var body: some View {
VStack {
FilteredList(type: filterType, filterKey: "lastName", filterValue: lastNameFilter, sortDescriptors: sortDescriptors) { (singer: Singer) in
Text("\(singer.wrappedFirstName) \(singer.wrappedLastName)")
}
Button("Add Examples") {
let taylor = Singer(context: moc)
taylor.firstName = "Taylor"
taylor.lastName = "Swift"
let ed = Singer(context: moc)
ed.firstName = "Ed"
ed.lastName = "Sheeran"
let adele = Singer(context: moc)
adele.firstName = "Adele"
adele.lastName = "Adkins"
try? moc.save()
}
Button("Show A") {
lastNameFilter = "A"
}
Button("Show S") {
lastNameFilter = "S"
}
Button("Begins with") {
filterType = .beginsWith
}
Button("Contains") {
filterType = .contains
}
Button("Sort A-Z") {
sortDescriptors = [SortDescriptor(\.firstName)]
}
Button("Sort Z-A") {
sortDescriptors = [SortDescriptor(\.firstName, order: .reverse)]
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Core Data SwiftUI challenge #3
The third and final challenge is to make FilteredList
accept an array of SortDescriptor
objects to get used in its fetch request.
//
// FilteredList.swift
// CoreDataProject
import CoreData
import SwiftUI
enum FilterType: String {
case beginsWith = "BEGINSWITH"
case contains = "CONTAINS[c]"
}
struct FilteredList<T: NSManagedObject, Content: View>: View {
@FetchRequest var fetchRequest: FetchedResults<T>
// this is our content closure; we'll call this once for each item in the list
let content: (T) -> Content
var body: some View {
List(fetchRequest, id: \.self) { singer in
self.content(singer)
}
}
init(type: FilterType = .contains, filterKey: String, filterValue: String, sortDescriptors: [SortDescriptor<T>] = [], @ViewBuilder content: @escaping (T) -> Content) {
_fetchRequest = FetchRequest<T>(sortDescriptors: sortDescriptors, predicate: NSPredicate(format: "%K \(type.rawValue) %@", filterKey, filterValue))
self.content = content
}
}
Wrap up
That’s it for day 59! It’s been a few long and difficult days and I’ll definitely have to loop over these days again in the future for a refresher, but that’s okay. It’s part of the learning process. We’ll be back tomorrow for day 60 and a consolidation day.
100 Days of SwiftUI – Day 59