SwiftUI and @FetchRequest - modify predicate or sort dynamically?

Has anyone had any luck using the @FetchRequest modifier to set up a set of @FetchedResults using SwiftUI and Core Data and then dynamically modifying those results during runtime by changing the predicate and/or sort descriptors? Is there any way to do this using an elegent approach like the @FetchRequest combine pipeline? Thanks!

Replies

Perhaps not an elegant solution, but you can set the fetch request in the init of your View.

Code Block swift
private var filteredFetchRequest: FetchRequest<Item>
private var filteredItems: FetchedResults<Item> {
filteredFetchRequest.wrappedValue
}
init(filter: String) {
filteredFetchRequest = FetchRequest<Item>(entity: Item.entity(), sortDescriptors: [], predicate: NSPredicate(format: "title BEGINSWITH %@", filter))
}


Maybe there's a better way to do it, but have a look: https://github.com/AlmightyBeaver/Dynamic-Predicate-CoreData-SwiftUI
Another way:

Code Block
@FetchRequest var filteredItems: FetchedResults<Item>
init(filter: String) {
_filteredItems = FetchRequest(sortDescriptors: [], predicate: NSPredicate(format: "title BEGINSWITH %@", filter))
}


Or you could pass a fetch request in from superview and it could even be @State there. Turn on SQL debugging and check how often you are hitting the database. I wish Apple would help us out here.
Post not yet marked as solved Up vote reply of malc Down vote reply of malc

Extending what has already been suggested, you can modify the predicate of the wrappedValue to dynamically trigger an update to a SwiftUI list. In the example below, toggling the isFiltered @State causes an update of the List and it reloads the FetchedResults with the filtered or unfiltered predicate.

@FetchRequest private var items: FetchedResults<Item>
@State var isFiltered = false
let filteredPredicate: NSPredicate
let unfilteredPredicate: NSPredicate

init(element: Element) {
     self.element = element
     filteredPredicate = NSPredicate(format: "element == %@ && score > 0.85", element)
     unfilteredPredicate = NSPredicate(format: "element == %@", element)

     self._items = FetchRequest<Item>(entity: Item.entity(),
                                      sortDescriptors: [NSSortDescriptor(keyPath: \Item.name, ascending: true),
                                      predicate: unfilteredPredicate,
                                      animation: .default)
}

var listItems: FetchedResults<Moment> {
     get {
          _moments.wrappedValue.nsPredicate = isFiltered ? filteredPredicate : unfilteredPredicate
          return moments
     }
}

var body: some View {
    List {
       ForEach(Array(listItems.enumerated()), id: \.element) { index, item in
          Text(item.name)
          .toolbar {
               ToolbarItem {
                   Button {
                       withAnimation {
                           isFiltered.toggle()
                       }
                   } label: {
                       Label("Filter Items", systemImage: isFiltered ? "star.circle.fill" : "star.circle")
                   }
               }
           }
        }
    }
}

After two years I have the solution you are looking for, but you probably don't need it anymore, but for others who may also be wondering the same thing here is an example of how to sort a FetchedResults type with a search bar

You'll ned to include this at the top of your struct. and you have to change "Item" to the name of the entity in your core data file.

@FetchRequest(sortDescriptors: []) var items: FetchedResults<Item>
@State private var searchingFor = ""

And link this to the end of your list or whatever contains your list. You'll have to change title to whatever the name of your attribute is in your core data file

.searchable(text: $searchingFor, placement: .navigationBarDrawer(displayMode: .always), prompt: "Search Titles")
.onChange(of: searchingFor){ value in
if (searchingFor != ""){
    items.nsPredicate=NSPredicate(format: "title CONTAINS[c] %@", searchingFor)
} else {
    items.nsPredicate=nil
}

Let me know if you have any questions!

@WeagleWeagle I have a scenario in which the initialiser to the view passes a value and depending on the value I need to determine the predicate and sort descriptors.

Currently I have an if else statement and then I am setting self._items as suggested by @aharriscrowne

For that scenario, is it ok to set self._items in the initialiser?

Add a Comment