0

I've been looking on how to search using a NSFetchResultController but most post I came across are from 2 years ago. Okay I'm trying to filter the objects, which in my case are Pokemon. Here is my function I'm using.

func attemptPokemonFetch(generation: String = "1", name: String = "") {
    let context = coreData.persistentContainer.viewContext

    let request: NSFetchRequest<Pokemon> = Pokemon.fetchRequest()
    let sortByName = NSSortDescriptor(key: "id", ascending: true)
    request.sortDescriptors = [sortByName]
    request.predicate = NSPredicate(format: "generation = %@", generation)
    if !name.isEmpty {
        request.predicate = NSPredicate(format: "name CONTAINS[cd] %@", name)
    }

     let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
    controller.delegate = self
    self.controller = controller

    do {
        try controller.performFetch()
    }
    catch let err {
        print(err)
    }
}

I call this function in view did load, search bar did change text and when the segment changes index value. I know how to filter the Pokemon and use the searching. However the problem I come across is when I begin searching. When searching for a Pokemon in a specific generation other Pokemon from another generation will also appear. So I'll be at index 2 which is for generation 2 Pokemon and when searching, other Pokemon from different generation will be appearing. I'm not sure if its how I initialize the context. This is how my search bar function look like.

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    if searchBar == pokemonSearchBar {
        let text = pokemonSearchBar.text!
        self.attemptPokemonFetch(name: text)
        collectionView.reloadData()
    }
}

Another Example: Lets say I have 3 types of people some are skinny, normal and fat. When I'm at the index for skinny people I only want to search for people that are skinny not all the people. So what would I need to do to achieve this type of behavior.

Would really appreciate any help. :)

Luis Ramirez
  • 966
  • 1
  • 8
  • 25

1 Answers1

1

In your code

request.predicate = NSPredicate(format: "generation = %@", generation)
if !name.isEmpty {
    request.predicate = NSPredicate(format: "name CONTAINS[cd] %@", name)
}

the second assignment replaces the previously assigned predicate. What you probably want is

if name.isEmpty {
    request.predicate = NSPredicate(format: "generation = %@", generation)
} else {
    request.predicate = NSPredicate(format: "generation = %@ AND name CONTAINS[cd] %@", generation, name)
}

A more flexible approach is to use NSCompoundPredicate:

var predicates = [NSPredicate]()
predicates.append(NSPredicate(format: "generation = %@", generation))
if !name.isEmpty {
    predicates.append(NSPredicate(format: "name CONTAINS[cd] %@", name))
}
request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicates)
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thank you Martin, just the behavior I wanted. I didn't know you can add AND to NSPredicate format. New to using predicates, so Im wondering if there can be an OR. For example let say in the search bar, I can let them search by name or by there pokemon Id. How would that work? – Luis Ramirez Apr 16 '17 at 07:16
  • @LuisRamirez: There is also an "OR". Look up the "Predicate Programming Guide" where you will find a description of the syntax. – Martin R Apr 16 '17 at 09:04