You can change your results based on a binding in your fetch predicate, but with Bool vars, I've found it is difficult to do. The reason is, the predicate to test a Bool in CoreData is something like NSPredicate(format: "myAttrib == YES")
whereas your Bool binding variable will be true or false, not YES or NO... So if you NSPredicate(format: "%K ==%@", #keypath(Entity.seeMe), seeMe.wrappedValue)
, this will always be false. Maybe I'm wrong, but this is what I've experienced.
You can filter your fetch based on String data easier.. But it works a little differently than my example below because your need to run your fetch in the init() of the View like this:
@Binding var searchTerm:String
var fetch: FetchRequest<Entity>
var rows: FetchedResults<Entity>{fetch.wrappedValue}
init(searchTerm:Binding<String>) {
self._searchTerm = searchTerm
self.fetch = FetchRequest(entity: Entity.entity(), sortDescriptors: [], predicate: NSPredicate(format: "%K == %@", #keyPath(Entity.attribute),searchTerm.wrappedValue))
}
To accomplish the task you've described, clicking on a bar button item thereby toggling a Bool, the below example is what I would recommend:
This example will accomplish your goal without changing the fetch predicate. It uses logic to decide whether or not to display a row of data based on the entry in the data model and the value of your @State variable.
import SwiftUI
import CoreData
import Combine
struct ContentView: View {
@Environment(\.managedObjectContext) var viewContext
@State var seeMe = false
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Entity.attribute, ascending: true)],
animation: .default)
var rows: FetchedResults<Entity>
var body: some View {
NavigationView {
VStack {
ForEach(self.rows, id: \.self) { row in
Group() {
if (self.validate(seeMe: row.seeMe)) {
Text(row.attribute!)
}
}
}
.navigationBarItems(leading:
Button(action: {
self.seeMe.toggle()
}) {
Text("SeeMe")
}
)
Button(action: {
Entity.create(in: self.viewContext, attribute: "See Me item", seeMe: true)
}) {
Text("add seeMe item")
}
Button(action: {
Entity.create(in: self.viewContext, attribute: "Dont See Me item", seeMe: false)
}) {
Text("add NON seeMe item")
}
}
}
}
func validate(seeMe: Bool) -> Bool {
if (self.seeMe && seeMe) {
return true
} else if (!self.seeMe && !seeMe ){
return true
} else {
return false
}
}
}
extension Entity {
static func create(in managedObjectContext: NSManagedObjectContext,
attribute: String,
seeMe: Bool
){
let newEvent = self.init(context: managedObjectContext)
newEvent.attribute = attribute
newEvent.seeMe = seeMe
}
static func save(in managedObjectContext: NSManagedObjectContext) {
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
To use this example, create a core data model with an entity named "Entity" and two attributes, one named 'attribute' as a String and the other named 'seeMe' as a Bool. Then run it, press the buttons to create the two types of data and then click the bar button item at the top to select which to display.
I'ts not the prettiest of examples, but it should demonstrate the functionality of what you are trying to accomplish.