How can i combine a mix AND OR condition in Swift predicate. I have a following query
Select * from tblTemp where dept == 1 && (subdept == 11 || subdept == 12)
I can write two predicate with same operator but don't know how to combine them
let deptPredicate = NSPredicate(format: "dept == %#", 1)
let subdeptPredicate1 = NSPredicate(format: "subdept = %#", 11)
let subdeptPredicate2 = NSPredicate(format: "subdept = %#", 12)
let andPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.and, subpredicates: [deptPredicate, subdeptPredicate1])
NSCompoundPredicate is a subclass of NSPredicate, which means
that the result of
NSCompoundPredicate(type:subpredicates:) can be used in another compound
predicate.
Note however that the %# format placeholder expects an NSObject
instance:
let deptPredicate = NSPredicate(format: "dept == %#", 1 as NSNumber)
let subdeptPredicate1 = NSPredicate(format: "subdept = %#", 11 as NSNumber)
let subdeptPredicate2 = NSPredicate(format: "subdept = %#", 12 as NSNumber)
let orPredicate = NSCompoundPredicate(type: .or,
subpredicates: [subdeptPredicate1, subdeptPredicate2])
let andPredicate = NSCompoundPredicate(type: .and,
subpredicates: [deptPredicate, orPredicate])
Alternatively, use the %ld format for integers:
let deptPredicate = NSPredicate(format: "dept == %ld", 1)
// ... etc.
There are also convenience initializers:
let orPredicate = NSCompoundPredicate(orPredicateWithSubpredicates:
[subdeptPredicate1, subdeptPredicate2])
let andPredicate = NSCompoundPredicate(andPredicateWithSubpredicates:
[deptPredicate, orPredicate])
Compound predicates are very useful to combine a dynamic set of
conditions at runtime. On the other hand, if only the values change
then you can simply use "AND" and "OR" within the predicate
format string:
NSPredicate(format: "dept == %ld AND (subdept = %ld OR subdept = %ld)", 1, 11, 12)
Finally note that you can use the #keyPath directive with the
%K placeholder, so that the compiler fills in the correct property
name (thus reducing the chance of typographical errors):
let deptPredicate = NSPredicate(format: "%K == %ld", #keyPath(MyEntity.dept), 1)
Related
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
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!)
Currently, I have a simple NSFetchRequest with an associated NSPredicate. However, Im hoping there is a way you can append multiple predicates. I've seen examples in Objective C, but none for Swift.
Can you define a list of NSPredicate's or append multiple NSPredicate objects to a single NSFetchRequest somehow?
Thanks!
You can use "NSCompoundPredicate". For example:
let converstationKeyPredicate = NSPredicate(format: "conversationKey = %#", conversationKey)
let messageKeyPredicate = NSPredicate(format: "messageKey = %#", messageKey)
let andPredicate = NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: [converstationKeyPredicate, messageKeyPredicate])
request.predicate = andPredicate
You can change into "AndPredicateType" or "OrPredicateType"
Update for Swift 4
let predicateIsNumber = NSPredicate(format: "isStringOrNumber == %#", NSNumber(value: false))
let predicateIsEnabled = NSPredicate(format: "isEnabled == %#", NSNumber(value: true))
let andPredicate = NSCompoundPredicate(type: .and, subpredicates: [predicateIsNumber, predicateIsEnabled])
//check here for the sender of the message
let fetchRequestSender = NSFetchRequest<NSFetchRequestResult>(entityName: "Keyword")
fetchRequestSender.predicate = andPredicate
The change in latest Swift Version is:
`NSCompoundPredicateType.AndPredicateType` replaced by `NSCompoundPredicate.LogicalType.and`
Hope it helps!!!
Thanks
AnyThe first statement doesn't return any values, while second one works correctly. Is it possible to specify property name as one of the arguments to avoid hard coding?
let predicate = NSPredicate(format: "%# == %#", "id","553178666d61d70c24fe4221")
let predicate = NSPredicate(format: "id == %#", "553178666d61d70c24fe4221")
That's a complete solution thanks to #FreeNickname
class func searchForObject(propertyName:NSString,property:NSString,className:String,single:Bool )->AnyObject?{
let fetchRequest = NSFetchRequest(entityName: className)
let predicate = NSPredicate(format: "%K == %#", argumentArray: [propertyName, property])
var appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
fetchRequest.predicate = predicate
// Execute the fetch request, and cast the results to an array of LogItem objects
var error:NSError?
if let fetchResults = appDelegate.managedObjectContext!.executeFetchRequest(fetchRequest, error: &error) as [AnyObject]? {
if let e = error{
println(e.debugDescription)
}
if single {
var object: AnyObject? = fetchResults.first
return object;
}
else{
return fetchResults
}
}
return nil;
}
Try %K instead of %# for the attribute name.
Like so:
let predicate = NSPredicate(format: "%K == %#", "id","553178666d61d70c24fe4221")
Source: Predicates Syntax (developer.apple.com)
From the docs:
The format string supports printf-style format specifiers such as %x (see Formatting String Objects). Two important format specifiers are %# and %K.
%# is a var arg substitution for an object value—often a string, number, or date.
%K is a var arg substitution for a key path.
I am trying to use NSPredicate in Swift to query Core Data but it throws an EXC_BAD_ACCESS(Code=1, address=0x1) error when trying to run it, what am I doing wrong?
Here is the file where the error happens
class LevelsScreenModel : UIViewController {
func getWord(level: Int, section: Int) -> String
{
let fetchRequest = NSFetchRequest(entityName: "Words")
//This is the line where the error happens
fetchRequest.predicate = NSPredicate(format: "level = %#", level)
fetchRequest.predicate = NSPredicate(format: "section = %#", section)
let word = AppDelegate().managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as [Words]
if(word.count > 1)
{
for words in word
{
println(words.word)
return words.word
}
}
return "ERROR"
}
}
The %# placeholder in predicate format strings is for Objective-C
objects, so you have to wrap the integer into an NSNumber:
fetchRequest.predicate = NSPredicate(format: "level = %#", NSNumber(integer: level))
or use ld instead to format a (long) integer:
fetchRequest.predicate = NSPredicate(format: "level = %ld", level)
Note also that
fetchRequest.predicate = NSPredicate(format: ...)
fetchRequest.predicate = NSPredicate(format: ...)
does not create a compound predicate, the seconds assignment simply
overwrites the first. You can use an NSCompoundPredicate:
let p1 = NSPredicate(format: "level = %ld", level)!
let p2 = NSPredicate(format: "section = %ld", section)!
fetchRequest.predicate = NSCompoundPredicate.andPredicateWithSubpredicates([p1, p2])
or simply combine the predicates with "AND":
fetchRequest.predicate = NSPredicate(format: "level = %ld AND section = %ld", level, section)
Instead of fussing around with format conversions and AND subpredicates, you could use the PredicatePal framework:
fetchRequest.predicate = *(Key("level") == level && Key("section") == section)
Note that you'll need to use == instead of = for equality comparison.