EntityA: Gymnast
firstName
lastName
one to many MeetResults
EntityB: MeetResults
meetDate
barScore
beamScore
floorScore
vaultScore
I am loading all gymnasts who are set as active into the picker. As I create an NSSET result.meetresults = meetScore.copy() as? NSSet which works fine I am trying to figure out how to remove a gymnast who already has a NSSET for this date. Any assistance would be appreciated.
func getGymnasts() {
let fetchRequest = NSFetchRequest(entityName: "Gymnast")
let sortDescriptor1 = NSSortDescriptor(key: "fullName", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor1]
let filter1 = NSPredicate(format: "isActive == %#", "Yes")
let filter2 = NSPredicate(format: "ANY meetresults.meetDate != %#", "\(meetDateText.text!)")
let predicate = NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: [filter1, filter2])
fetchRequest.predicate = predicate
//fetchRequest.predicate = filter1
do {
self.gymnastArray = try AD.managedObjectContext.executeFetchRequest(fetchRequest) as! [Gymnast]
self.gymnastPicker.reloadAllComponents()
} catch {
fatalError("Fetch Failed")
}
}
To-many relationships are awkward sometimes. You can use the subquery syntax to query the count of an internal fetch to find Meets matching your criteria, then check there aren't any:
let filter2 = NSPredicate(format: "SUBQUERY(meetresults.meetDate, $m, $m.meetDate == %#).#count == 0", meetDateText.text!)
Related
I have a string UUID coming into this method, to lookup an entity in CoreData that has UUID's saved as UUID type (Not String).
I keep getting "Fatal error: Unexpectedly found nil while unwrapping an Optional value" on line for the predicate.
func loadUser(uuid: String) -> [ExistingUsers2] {
let request : NSFetchRequest<ExistingUsers2> = ExistingUsers2.fetchRequest()
let uuidQuery = NSUUID(uuidString: uuid)
request.predicate = NSPredicate(format: "%K == %#", #keyPath(ExistingUsers2.uuid), uuidQuery! as CVarArg)
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
do {
existingUsersArray = try context.fetch(request)
print("Returned \(existingUsersArray.count)")
} catch {
print("Error fetching data from context \(error)")
}
return existingUsersArray
}
Any help? I haven't found anything here or Dr Google. TKS
You can replace your predicate with this:
guard let uuidQuery = UUID(uuidString: uuid) else { return [] } // no valid UUID with this code
request.predicate = NSPredicate(format: "%K == %#", #keyPath(ExistingUsers2.uuid), uuidQuery as CVarArg)
Everything else should work.
UPDATE
This is the code that finally worked, thanks for your help #André Henrique da Silva
func loadUser(uuid: String) -> [ExistingUsers2] {
let request : NSFetchRequest<ExistingUsers2> = ExistingUsers2.fetchRequest()
let uuidQuery = NSUUID(uuidString: uuid)
request.predicate = NSPredicate(format: "uuid == %#", uuidQuery! as CVarArg)
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
do {
existingUsersArray = try context.fetch(request)
} catch {
print("Error fetching data from context \(error)")
}
return existingUsersArray
}
Try this as your predicate: NSPredicate(format: "cid = %#", "\(id)")
where cid is the UUID in CoreData and id is the UUID you got from the string. Also do not use NSUUID.
Replace the uuidAttributeName with your attribute name and yourStringuuid with the your string that you want to convert into UUID type.
var uuid = UUID(uuidString: yourStringuuid)
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "ExistingUsers2")
fetchRequest.predicate = NSPredicate(format: "uuidAttributeName == %#",uuid)
let results = try context.fetch(fetchRequest)
My Code
isFiltering = true
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let words = textInSearchField.components(separatedBy: " ")
for word in words{
if (word).count == 0{
continue
}
let firstNamePredicate = NSPredicate(format: "firstName contains[c] %#", word)
let lastNamePredicate = NSPredicate(format: "lastName contains[c] %#", word)
let idPredicate = NSPredicate(format: "id contains[c] %#", word)
let orPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.or, subpredicates: [firstNamePredicate, lastNamePredicate, idPredicate])
clientsEntity.predicate = orPredicate
clientResults = try! context.fetch(clientsEntity) as! [NSManagedObject]
let sort:NSSortDescriptor = NSSortDescriptor(key:"dateSorted", ascending: false)
for (index, ob) in clientResults.enumerated(){
let relationship = ob.value(forKey: "assessed_by") as! NSSet
let array = relationship.sortedArray(using: [sort]) as! [NSManagedObject]
for item in array.enumerated() {
results.append(item.element)
print(results)
}
}
My data model:
I am using a tableView to display my data which works great, now I have implemented a filter function which allows the user to search based on a Clients first name, last name, id etc using NSCompoundPredicate.
I then sort the resulting [NSManagedObject] by date using NSSortDescriptor, my aim is to set my clientResults variable to contain the SORTED contents of the NSSet. My print statement only outputs that there is one Assessment inside the results variable when in actual fact the NSSet contains two of these NSManagedObjects.
let sort:NSSortDescriptor = NSSortDescriptor(key:"dateSorted", ascending: false)
for (index, ob) in clientResults.enumerated(){
let relationship = ob.value(forKey: "assessed_by") as! NSSet
let array = relationship.sortedArray(using: [sort]) as! [NSManagedObject]
// MARK - I enumerate the contents of the sorted array.
for item in array.enumerated() {
results.append(item.element)
print(results)
}
}
What is the best practice for assigning the contents of the NSSet to a variable of type [NSManagedObject]?
Thank you.
If you know that elements in NSSet are of type NSManagedObject why not just do
let managedObjectsArray = set.allObjects
or if you want to make sure it is of correct type you can do:
if let managedObjectsArray = set.allObjects as? [NSManagedObject] {
//do what you want with [NSManagedObject] array
}
In my code below I am using NSPredicates specifically I am using NSCompoundPredicate to check for multiple different parameters in my search function. How would I go about searching with both the First Name AND Last Name, I am currently using AND but it does not return anything in my UITableView. All my other predicates listed in the compound predicate work great.
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
textInSearchField = searchBar.text!
if searchBar.text == ""{
print("Searching for all clients.")
retrieveClients()
view.endEditing(true)
clientTableView.reloadData()
}else{
print("Enter a client name to search.")
isFilteringSearch = true
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let firstAndLast = NSPredicate(format: "firstName = %# AND lastName = %#", textInSearchField)
let firstNamePredicate = NSPredicate(format: "firstName = %#", textInSearchField)
let lastNamePredicate = NSPredicate(format: "lastName = %#", textInSearchField)
let idPredicate = NSPredicate(format: "id = %#", textInSearchField)
let orPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.or, subpredicates: [firstAndLast,firstNamePredicate, lastNamePredicate, idPredicate])
clientsEntity.predicate = orPredicate
clients = try! context.fetch(clientsEntity) as! [NSManagedObject]
view.endEditing(true)
clientTableView.reloadData()
}
}
Something to bear in mind is that I still need to be able to use
LogicalType.or since I want to have the option for the user to search by first name, last name but also a combination of both for example Harold Finch or Finch/Harold etc.
Cheers!
let firstAndLast = NSPredicate(format: "firstName = %# AND lastName = %#", textInSearchField)
let firstNamePredicate = NSPredicate(format: "firstName = %#", textInSearchField)
let lastNamePredicate = NSPredicate(format: "lastName = %#", textInSearchField)
let idPredicate = NSPredicate(format: "id = %#", textInSearchField)
let orPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.or, subpredicates: [firstAndLast,firstNamePredicate, lastNamePredicate, idPredicate])
I hope that for orPredicate, it should be [firstAndLast, idPredicate] instead, but you didn't make it work. Else you need to rethink about it, that's your orPredicate current logic:
if ((a == x AND b == x) OR (a == x) OR (b == x) OR (c = x)) {
}
The (a == x AND b == x) is useless.
There is an issue on that line:
let firstAndLast = NSPredicate(format: "firstName = %# AND lastName = %#", textInSearchField)
You have two placeholders (%#), but only one argument (textInSearchField).
let firstAndLast = NSPredicate(format: "firstName = %# AND lastName = %#", textInSearchField, textInSearchField)
Thank for the help, I ended up using an alternative method with contains[c] my code is below for anyone who needs help with a similar function, you can use contains[c](contains character) to search via character specifically this applies to all strings i.e first name or last name and in my case also an ID which is stored as a String also.
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
textInSearchField = searchBar.text!
if searchBar.text == ""{
retrieveClients()
view.endEditing(true)
clientTableView.reloadData()
}else{
isFilteringSearch = true
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let words = textInSearchField.components(separatedBy: " ")
for word in words{
if (word).count == 0{
continue
}
let firstNamePredicate = NSPredicate(format: "firstName contains[c] %#", word)
let lastNamePredicate = NSPredicate(format: "lastName contains[c] %#", word)
let idPredicate = NSPredicate(format: "id contains[c] %#", word)
let orPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.or, subpredicates: [firstNamePredicate, lastNamePredicate, idPredicate])
clientsEntity.predicate = orPredicate
}
clients = try! context.fetch(clientsEntity) as! [NSManagedObject]
view.endEditing(true)
clientTableView.reloadData()
}
}
I followed the second answer on this post NSPredicate: Combine CONTAINS with IN
Thank You
I am attempting to filter the items in my FetchedResultsController to filter out a certain set of items. These are engines which have a certain letter designation in them followed by numbers. The possible rage of letters in the designation are from "A" to "O". I would like to be able to filter out engines that have the designations from "D" to "O" based on another condition.
I set a constant "highPowerEngines" that is a set containing those letters. Since the designation only contains one letter, I want to exclude any designation that contains any of the letters in highPowerEngines. So far, this is my code, the predicate I am working on is contained after the FIXME:
func configureFetchedResultsController() {
let context = databaseController.getContext()
let enginesFetchRequest = NSFetchRequest<Engine>(entityName: CoreData.engine)
let highPowerEngines: Set = ["D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"]
var predicate = NSPredicate()
if currentStage == 1 && stages! == 1 {
predicate = NSPredicate(format: "engineType == %#", EngineType.singleStage.rawValue)
} else if currentStage < stages! {
predicate = NSPredicate(format: "engineType == %#", EngineType.boosterStage.rawValue)
} else {
predicate = NSPredicate(format: "engineType == %#", EngineType.upperStage.rawValue)
}
var predicateArray:[NSPredicate] = [
predicate
]
// FIXME: Sort out highPowerEngines
if !dPlusEngineIAP {
if currentStage == 1 && stages! == 1 {
predicate = NSPredicate(format: "NOT engineDesignation CONTAINS %#", highPowerEngines)
predicateArray.append(predicate)
}
}
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicateArray)
enginesFetchRequest.predicate = compoundPredicate
let primarySortDescriptor = NSSortDescriptor(key: CoreData.isMadeByName, ascending: true)
let secondarySortDescriptor = NSSortDescriptor(key: CoreData.engineDesignation, ascending: true)
enginesFetchRequest.sortDescriptors = [primarySortDescriptor, secondarySortDescriptor]
self.fetchedResultsController = NSFetchedResultsController<Engine>(
fetchRequest: enginesFetchRequest,
managedObjectContext: context,
sectionNameKeyPath: CoreData.isMadeByName,
cacheName: nil)
self.fetchedResultsController.delegate = self
}
Use IN instead of CONTAINS
NOT (engineDesignation IN %#)
update
Since you want to exclude all but tree letters why not turn the predicate around to include rather than exclude, something like
engineDesignation BEGINSWITH ‘A’ OR
engineDesignation BEGINSWITH ‘B’ OR
engineDesignation BEGINSWITH ‘C’
Thanks to the post here: CoreData Predicate get every sentence that contains any word in array
, I was finally able to solve my problem. My current working code is here:
func configureFetchedResultsController() {
let context = databaseController.getContext()
let enginesFetchRequest = NSFetchRequest<Engine>(entityName: CoreData.engine)
let lowPowerEngines = ["A", "B", "C"]
var predicate = NSPredicate()
if currentStage == 1 && stages! == 1 {
predicate = NSPredicate(format: "engineType == %#", EngineType.singleStage.rawValue)
} else if currentStage < stages! {
predicate = NSPredicate(format: "engineType == %#", EngineType.boosterStage.rawValue)
} else {
predicate = NSPredicate(format: "engineType == %#", EngineType.upperStage.rawValue)
}
var predicateArray:[NSPredicate] = [
predicate
]
if !dPlusEngineIAP {
let predicates = lowPowerEngines.map {
NSPredicate(format: "engineDesignation CONTAINS %#", $0)
}
let predicate = NSCompoundPredicate(orPredicateWithSubpredicates: predicates)
predicateArray.append(predicate)
}
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicateArray)
enginesFetchRequest.predicate = compoundPredicate
let primarySortDescriptor = NSSortDescriptor(key: CoreData.isMadeByName, ascending: true)
let secondarySortDescriptor = NSSortDescriptor(key: CoreData.engineDesignation, ascending: true)
enginesFetchRequest.sortDescriptors = [primarySortDescriptor, secondarySortDescriptor]
self.fetchedResultsController = NSFetchedResultsController<Engine>(
fetchRequest: enginesFetchRequest,
managedObjectContext: context,
sectionNameKeyPath: CoreData.isMadeByName,
cacheName: nil)
self.fetchedResultsController.delegate = self
}
The solution to dealing with each item in the array was to make a compound predicate using the map function. Then adding the compound predicate to the fetchedResultsController. That allows me to compare the engine to see if it is in lowPowerEngines. Map makes a separate predicate for each item in lowPowerEngines. I could even change it programmatically on the fly. I really hope this helps someone as there is no central place to come up with these sort of tricks. When I collect enough of them, I will do a master post.
Which predicate i need to use where objects in returned from Core Data array:
First objects must match completely;
Other object must just contained the specific word;
For example:
I have entity Man(firstName:String, lastName: String).
Let's say, i have this objects in Core Data:
1) Man(firstName: "John", secondName: "Alexandrov"), 2) Man(firstName: "Alex", secondName: "Kombarov"), 3) Man(firstName: "Felps", secondName: "Alexan").
And in returned arr i want to see [Man(firstName: "Alex", secondName: "Kombarov"), Man(firstName: "Felps", secondName: "Alexan"), Man(firstName: "John", secondName: "Alexandrov")]
How can i achieved this?
You could use a NSCompoundPredicate.
First, you'd create a predicate for the firstName. This one would be strict, so you'd search for matches using ==:
let firstNamePredicate = NSPredicate(format: "%K == %#", argumentArray: [#keyPath(Man.firstName), "alex"])
Then, you'd create a predicate for the lastName. This one is less strict, so you'd use CONTAINS:
let lastNamePredicate = NSPredicate(format: "%K CONTAINS[c] %#", argumentArray: [#keyPath(Man.lastName), "alex"])
Then you'd create an NSCompoundPredicate using the orPredicateWithSubpredicates signature.
let compoundPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: [firstNamePredicate, lastNamePredicate])
From there, you could create a NSFetchRequest and assign compoundPredicate as the predicate for the fetchRequest.
If you want to sort the results, you can add one or more NSSortDescriptors to your NSFetchRequest:
let sortByLastName = NSSortDescriptor(key: #keyPath(Man.lastName), ascending: true)
let sortByFirstName = NSSortDescriptor(key: #keyPath(Man.firstName), ascending: true)
request.sortDescriptors = [sortByLastName, sortByFirstName]
Then, you'd do the fetch:
let request: NSFetchRequest = Man.fetchRequest()
request.predicate = compoundPredicate
var results: [Man] = []
do {
results = try context.fetch(request)
} catch {
print("Something went horribly wrong!")
}
Here's a link to a useful post on NSPredicate
Adding to #Adrian answer, I had to make couple changes for it to work.
let FIRSTNAME = "Alex"
let LASTNAME = "Smith"
let firstNamePredicate = NSPredicate(format: "firstName == %#", FIRSTNAME)
let lastNamePredicate = NSPredicate(format: "firstName == %#", LASTNAME)
let compoundPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: [firstNamePredicate, lastNamePredicate])
request.predicate = compoundPredicate
do {
results = try context.fetch(request)
} catch {
print("Something went horribly wrong!")
}