NSPredicate filter by more than one string - swift

How do you make an NSPredicate that filters more than one object at the same time? The following works perfect for one object (in this case KDED) but I'm trying to get for example KDED, KDAB and KOMN at the same time, using swift2.
let fetchRequest = NSFetchRequest(entityName: "Airport")
let sortDescriptor = NSSortDescriptor(key: "code", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let predicate = NSPredicate(format: "code ==%#", "KDED")
fetchRequest.predicate = predicate
if let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext {
fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
fetchResultController.delegate = self
do {
try fetchResultController.performFetch()
Airports = fetchResultController.fetchedObjects as! [Airport]
} catch {
print(error)
}
}

Use the IN operator:
let predicate = NSPredicate(format: "code IN %#", ["KDED", "KDAB", "KOMN"])

Related

Core Data - NSFetchedResultsController using NSPredicate

I have Parent entity called Card and child entity CardHistoryItem with the to-many relationship. I'm trying to fetch histories for my current selected card, but it returns nothing just empty array.
My code:
lazy var cardHistoryItemsFRC: NSFetchedResultsController<CardHistoryItem> = {
let fetchRequest = NSFetchRequest<CardHistoryItem>(entityName: "CardHistoryItem")
if let currentCard = self.currentEntity as? Card {
fetchRequest.predicate = NSPredicate(format: "card = %#", currentCard)
}
let sort = NSSortDescriptor(key: "date", ascending: true)
fetchRequest.sortDescriptors = [sort]
fetchRequest.returnsObjectsAsFaults = false
let mainContext = CoreDataController.sharedInstance.mainContext
let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: mainContext, sectionNameKeyPath: nil, cacheName: nil)
frc.delegate = self
do {
try frc.performFetch()
} catch {
fatalError("NSFetchedResultsController perform fetch error!")
}
return frc
}()
Can someone please help me to find correct predicate?
Thanks

NSFetchedResultsController and Searching - Return multiple and single sections?

EDIT:
Shout out to pbasdf for helping me solve this issue.
Fixed code:
lazy var fetchedResultsController = self.getFetchedResultsController()
var sectionKeyPath: String? = #keyPath(Object.sectionKey)
var searchPredicate: NSCompoundPredicate?
// MARK: - Return FRC:
private func getFetchedResultsController() -> NSFetchedResultsController<Object> { // var fetchedResultsController:
print("Lazy: getFetchedResultsController()")
let fetchRequest: NSFetchRequest<Object> = Object.fetchRequest()
fetchRequest.predicate = searchPredicate
let sortByKey = NSSortDescriptor(key: #keyPath(Object.sectionKey), ascending: true)
let sortByName = NSSortDescriptor(key: #keyPath(Object.name), ascending: true)
fetchRequest.sortDescriptors = [sortByKey, sortByName]
fetchRequest.fetchBatchSize = 20
let fetchedResultsController = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: coreDataStack.managedContext,
sectionNameKeyPath: sectionKeyPath ?? nil,
cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController
}
private func refreshFRC() {
fetchedResultsController = getFetchedResultsController() // Reset FRC
do { // Load Data:
try fetchedResultsController.performFetch()
} catch let error as NSError {
print("Fetching error: \(error), \(error.userInfo)")
}
}
This gives you the FRC with an optional predicate and sectionNameKeyPath. Which you can then set to your needs, and then set the changes with refreshFRC().
I'm working on adding search to a tableview using a NSFetchedResultsController. My goal:
Return multiple sections based on first letter of object
Return all objects into a single section when searching.
I have working code. And I can make the table do both depending on my sectionKey, I just cant figure out how to do both in the same build.
Is this normal behavior and I'm trying to do something thats not possible by changing the FRC's sectionNameKeyPath and sortDescriptors? Or am I just missing something?
private func getFetchedResultsController() -> NSFetchedResultsController<Object> {
let fetchRequest: NSFetchRequest<Object> = Object.fetchRequest()
let sortByKey = NSSortDescriptor(key: #keyPath(Object.sectionKey), ascending: true)
let sortByName = NSSortDescriptor(key: #keyPath(Object.name), ascending: true)
switch sectionKeyPath {
case nil:
fetchRequest.sortDescriptors = nil
fetchRequest.fetchBatchSize = 20
default:
fetchRequest.sortDescriptors = [sortByKey, sortByName]
fetchRequest.fetchBatchSize = 20
}
fetchRequest.sortDescriptors = [sortByKey, sortByName]
fetchRequest.fetchBatchSize = 20
let fetchedResultsController = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: coreDataStack.managedContext,
sectionNameKeyPath: sectionKeyPath ?? nil,
cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController
}
I'm also curious if it's better to use a a single FRC for the entire viewController, or if it would be a better approach to make one for the entire list of objects, and a second only for when the search is active?
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
searchBar.barStyle = .default
switch searchBar.text?.count {
case nil:
searchPredicate = nil
sectionKeyPath = #keyPath(Object.sectionKey)
tableView.reloadData()
case 0:
searchPredicate = nil
sectionKeyPath = #keyPath(Object.sectionKey)
tableView.reloadData()
default:
sectionKeyPath = nil
guard let searchText = searchBar.text else { return }
setSearchPredicate(search: searchText)
}
fetchFRC()
tableView.reloadData()
} // End: updateSearchResults()
func fetchFRC() {
do {
try fetchedResultsController.performFetch()
} catch let error as NSError {
print("Fetching error: \(error), \(error.userInfo)")
}
}
As per comments:
You need to re-call getFetchedResultsController before fetchFRC in that updateSearchResults code; and
You need to assign the result to the fetchedResultsController var defined in your view controller.

Swift sort FetchRequest result by Turkish locale

How can I sort fetchResult by Turkish language, turkish characters sorted at the end of results.
let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext
let fetch = NSFetchRequest<Contact>(entityName: "Contact")
let sortDescriptor = NSSortDescriptor(key: "firstName", ascending: true)
let sortDescriptors = [sortDescriptor]
fetch.sortDescriptors = sortDescriptors
do {
let list = try managedObjectContext.fetch(fetch)
} catch {
fatalError("Failed \(error)")
}
Working code:
let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext
let fetch = NSFetchRequest<Contact>(entityName: "Contact")
let sortDescriptor = NSSortDescriptor(key: "firstName", ascending: true, selector: #selector(NSString.localizedCaseInsensitiveCompare))
let sortDescriptors = [sortDescriptor]
fetch.sortDescriptors = sortDescriptors
do {
let list = try managedObjectContext.fetch(fetch)
} catch {
fatalError("Failed \(error)")
}

Using NSPredicate to Sort Core Data in a Table View (Swift)

I am new to Core Data in Swift and need help using NSPredicate. I currently have a table view and a search bar in my app. I also have an entity called Item with an attribute of allergen (string). I want to filter this table view so that cells only display if searchBar.text is equal to Item.allergen.
func attemptFetch() {
let fetchRequest: NSFetchRequest<Item> = Item.fetchRequest()
let dateSort = NSSortDescriptor(key: "created", ascending: false)
let titleSort = NSSortDescriptor(key: "title", ascending: true)
if segment.selectedSegmentIndex == 0 {
fetchRequest.sortDescriptors = [dateSort]
} else if segment.selectedSegmentIndex == 1 {
fetchRequest.sortDescriptors = [titleSort]
} else {
if searchBar.text != nil, searchBar.text != ""{
print("Search bar text exists")
print(searchBar.text!)
fetchRequest.sortDescriptors = [titleSort]
//Search; Only display cell if searchBar.text = Item.allergen
} else {
print("Search bar text does not exist!")
fetchRequest.sortDescriptors = [titleSort]
}
}
let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = self
self.controller = controller
do {
try controller.performFetch()
} catch {
let error = error as NSError
print("\(error)")
}
}
I attempted to use NSPredicate to do this, but it resulted in a key path not found in entity error. I would include this code, but I'm sure it was entirely wrong.
Any advice?
Update:
Here's a picture of the Item entity's attributes in the Core Data Model.This is the code in the ItemEntity.swift file, I think this was autogenerated? Hopefully this is what you needed.
Update 2:
Thanks for the help! I found a solution. This is the code that worked for me:
let userSearch = searchBar.text!
commitPredicate = NSPredicate(format: "allergen == %#", userSearch)
fetchRequest.predicate = commitPredicate
Add the following predicate to filter only those which exactly match your search text:
fetchRequest.predicate = NSPredicate(format:"allergen == %#", searchBar.text)
Alternatively, you might want to match if the allergen string contains the search text:
fetchRequest.predicate = NSPredicate(format:"allergen CONTAINS %#", searchBar.text)
To make the comparison case and diacritic insensitive, add [cd] (so ... ==[cd] ... or ... CONTAINS[cd] ...).

Core Data Search predicate not searching symbols in predicate

Trying to implement a search feature into my application. The basic search function for regular card names containing no symbols works but, I have a few cards with names that contain symbols that don't search even if you put the appropriate card name in containing those symbols. Also having some slight difficulty trying to have it so when the text field is empty the full list returns. The search I have implemented is:
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
searchBar.setShowsCancelButton(true, animated: true)
let requiredClassName = passedDeckObject!.classSelected.classname
var requiredCardClasses = [Int]()
switch(requiredClassName) {
case "Warrior":
requiredCardClasses = [1,10]
case "Shaman":
requiredCardClasses = [2,10]
case "Rogue":
requiredCardClasses = [3,10]
case "Paladin":
requiredCardClasses = [4,10]
case "Hunter":
requiredCardClasses = [5,10]
case "Druid":
requiredCardClasses = [6,10]
case "Warlock":
requiredCardClasses = [7,10]
case "Mage":
requiredCardClasses = [8,10]
case "Priest":
requiredCardClasses = [9,10]
default:
requiredCardClasses = [10]
}
if !searchText.isEmpty {
// Clear out the fetchedResultController
// Setup the fetch request
let fetchRequest = NSFetchRequest(entityName: "Cards")
fetchRequest.fetchLimit = 50
fetchRequest.predicate = NSCompoundPredicate(type: .AndPredicateType, subpredicates: [NSPredicate(format: "name contains[cd] %#", searchText), NSPredicate(format: "cardClass IN %#", requiredCardClasses)])
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
//end//
//cost fetch request begins here//
let costfetchRequest = NSFetchRequest(entityName: "cardCost")
costfetchRequest.fetchLimit = 50
costfetchRequest.predicate = NSPredicate(format: "cardCost = %i", searchText)
let costSortDescriptor = NSSortDescriptor(key: "cardCost", ascending: true)
costfetchRequest.sortDescriptors = [sortDescriptor]
//end//
// Pass the fetchRequest and the context as parameters to the fetchedResultController
frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext:managedObjectContext!,
sectionNameKeyPath: nil, cacheName: nil)
// Make the fetchedResultController a delegate of the MoviesViewController class
frc.delegate = self
// Execute the fetch request or display an error message in the Debugger console
var error: NSError? = nil
if (!frc.performFetch(&error)) {
println("Error: \(error?.localizedDescription)")
}
// Refresh the table view to show search results
cardsListed.reloadData()
}
}