100 Days of SwiftUI Day 59

100 Days of SwiftUI – Day 59

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.

Darryl

Hi! My name is Darryl and this is my personal blog where I write about my journey as I learn programming! You'll also find articles about other things that interest me including games, tech and anime.

Post navigation

Leave a Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

100 Days of SwiftUI – Day 83

100 Days of SwiftUI – Day 93

100 Days of SwiftUI – Day 16 – First Project

100 Days of SwiftUI – Day 80