NSPredicate in swift for empty string - swift

Swift 4.2 iOS 11.x
Trying to read the records in a iCloud database that have a lineOwner field set, but struggling to create an NSPredicate that works!
This looks ok, but doesn't parse.
let predicate = NSPredicate(format: remoteAttributes.lineOwner + " != %#",0)
I get an invalid predicate error message [and a nasty crash into the bargain]. Spend a couple of hours on this and losing the will to live.
If I print out the database I see this.

You can simply compare to the empty String, "". The %# placeholder represents a String, so the crash happens because you supply an Int to the NSPredicate instead of a String.
You should also use the %K placeholder for variable names instead of appending strings.
let predicate = NSPredicate(format: " %K != %#", remoteAttributes.lineOwner, "")
If you also want to filter out nil values, you can use a compound predicate:
let predicate = NSPredicate(format: " %K != %# AND %K != nil", remoteAttributes.lineOwner, "")

Related

NSPredicate for searching a string with double quotes

An attribute element in coredata model is a string(json string format), which is an array of dictionaries like below,
one element has
"[{"tagName":"sad","count":2},{“tagName":"happy","count":1}]"
and other has
"[{"tagName":"sad1","count":2},{“tagName":"happy1","count":1},{“tagName":"nothappy","count":1}]"
Need to search the list with refer to the tagname.
If I use the predicate below,
tagName = "sad"
tagNameFilter += String(format: "vrTags CONTAINS[cd] \"%#\"", tagName)
it's returning both elements. It should return the first element alone
If I use without double quotes
tagName = "sad"
tagNameFilter += String(format: "vrTags CONTAINS[cd] %#", tagName)
it's crashing with reason:
unimplemented SQL generation for predicate : (vrTags CONTAINS[cd] sad) (LHS and RHS both keypaths) with userInfo of (null)
If I use
tagName = "sad"
tagNameFilter += String(format: "vrTags CONTAINS[cd] \"\"%#\"\"", tagName)
it's crashing with reason: Unable to parse the format string
How to solve this filter issue? Any suggestions would be appreciated.
In order to search for a string "sad" including the quotation marks you have to pass that string with the quotation marks as an argument to NSPredicate(format:). This can be done with with string interpolation:
let tagName = "sad"
let predicate = NSPredicate(format: "vrTags CONTAINS[cd] %#", "\"\(tagName)\"")
print(predicate) // vrTags CONTAINS[cd] "\"sad\""
And never use String(format:) and string concatenation to build complex predicates. That is very error-prone because the quoting and escaping rules for predicate strings are different from those to format strings.
If you need to combine multiple conditions with “AND” then do it like
let p1 = NSPredicate(...)
let p2 = NSPredicate(...)
// ...
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [p1, p2, ...])

Core Data - why is my NSPredicate not resulting in the correct SQL query?

I have tried both these NSPredicates:
let predicate = NSPredicate(format: “personID == %lld", id)
let predicate = NSPredicate(format: “personID == %d", id)
I enabled SQL logging and saw this at the end of the SQL query:
WHERE t0.ZPERSONID = ?
In variables view, id has a value of Int64(156), but regardless of what I put in place of id, the resulting SQL query has t0.ZPERSONID = ? for its WHERE clause. Because of this, the predicate is useless and I’m getting duplicate record insertions every time. How do I get t0.ZPERSONID = 156?
WHERE t0.ZPERSONID = ?
is (part of) the prepared statement. To see which values the parameters are bound to, set the Core Data debugging level to 3 (compare How to print Core Data debug values?):
-com.apple.CoreData.SQLDebug 3
-com.apple.CoreData.Logging.stderr 1
Also print(predicate) is useful to check the created predicate
for correctness.
In your case the problem is that you pass an NSNumber instance
as argument, but the %lld format expects an Int64.
You can convert the NSNumber to an Int64
let id = NSNumber(value: Int64(156))
let p1 = NSPredicate(format: "personID == %lld", id.int64Value)
print(p1) // personID == 156
or even simpler, use the %# format instead:
let p2 = NSPredicate(format: "personID == %#", id)
print(p2) // personID == 156

NSPredicate: Unable to parse the format string for relationship

I have two entity named "CIUser" and "CICast". "CIUser" entity has an one-to-one relationship with "CICast" named "cast".
CIUser :
-> userId(Int)
-> isLive(bool)
-> name(String)
CICast:
-> castId(Int)
-> lastUpdate(Date)
Now my requirement is to fetch all the users who are currently live and there lastUpdate is less than a calculated date. So I had prepared my fetch request like
let time = //an calculated NSDate object
let fetchRequest = NSFetchRequest(entityName: "CIUser")
fetchRequest.predicate = NSPredicate(format: "isLive == %# AND cast.lastUpdate <= %#", NSNumber(bool: true), time)
But it crash the application throwing *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "isLive == %# AND cast.lastUpdate <= %#"'
Can anyone help me where I am doing wrong, or what approach should I take. Suggestion will be appreciated.
The problem is that "CAST" is a reserved word in the predicate format
syntax, and the reserved words are case-insensitive. So this conflicts
with your relationship named "cast".
As a workaround, use the %K key path substitution:
fetchRequest.predicate = NSPredicate(format: "isLive == %# AND %K <= %#",
NSNumber(bool: true), "cast.lastUpdate", time)
or rename the relationship. You may want to use the %K expansion
generally to avoid such problems:
fetchRequest.predicate = NSPredicate(format: "%K == %# AND %K <= %#",
"isLive", NSNumber(bool: true),
"cast.lastUpdate", time)
For more information, see Predicate Format String Syntax
in the "Predicate Programming Guide".

CoreData: NSPredicate filter by date

I have a dataset which contains the following date 6/14, 6/15, 6/16, 6/17 and 6/18
I have two predicates
let today = NSDate()
NSPredicate(format: "checked == %# AND dateTime >= %#", NSNumber(bool: false), today)
NSPredicate(format: "checked == %# AND dateTime < %#", NSNumber(bool: false), today)
let say today is 6/16 so according to the first predicate, fetchRequest should pick only 6/16, 6/17 and 6/18 and for second predicate, fetchRequest should only pick 6/14 and 6/15.
But the problem is first predicate pick all the rows and second one return blank.
DataModel
conversion code
Any idea what's going wrong here.
In your model you set the dateTime property as a Date object, but you saved it as a double.
So you have to do:
dateTime = NSDate(dateWithTimeIntervalSinceReferenceDate:Double(activityInfo["eventDateTime‌​"] as! String))
Note, I don't speak Swift, so I don't know if this code could be shorter, but you get the idea.

Searchbar with core data issues

I have a search bar.And data displayed in labels with scrollview.
For ex:
core data Fields :
1.id
2.company
3.Employe Name
4.Address
If i type id,company or Employee Name in searchbar i want to dispaly associated results.
my code :
For search data :
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
var request = NSFetchRequest(entityName: "Agency")
request.returnsObjectsAsFaults = false
var countResult : NSArray = context.executeFetchRequest(request, error: nil)!
let result = NSPredicate(format: "SELF CONTAINS[c] %#",searchText)
self.filtered = self.countResult.filteredArrayUsingPredicate(result!)
if (filtered.count == 0 ) {
searchActive = false;
}else {
searchActive = true;
}
println(filtered)
}
It shows an error " 'Can't use in/contains operator with collection".
These codes cannot satisfy what i want.And also i dont have a idea how to fetch the related rows according to enter value in search bar.
Thanks in Advance.
The first problem is your predicate - you're trying to use CONTAINS on an NSManagedObject subclass, but CONTAINS only works with String. To check whether your search text is contained within any of your managed objects you need to evaluate whether it is contained in each attribute (in your case id, company and empolyeeName, I'm assuming they're all Strings).
To do this you should change your predicate to:
let searchPredicate = NSPredicate(format: "id BEGINSWITH %# OR
company BEGINSWITH %# OR
employeeName BEGINSWITH %#",
searchText, searchText, searchText)
I would recommend using BEGINSWITH instead of CONTAINS[c] since when searching your user is likely to be entering the first part of the phrase. Also, as Apple said in their 2013 WWDC talk Core Data Performance Optimization and Debugging -
...we've got begins with and ends with and that's by far the cheapest query that you can execute.
...
Contains is more expensive because we have to work along and see
whether it contains...
And in the case of a search, you want it to be fast!
Secondly, you don't need to filter your results after getting them back from CoreData. You can set the predicate property on your NSFetchRequest and your returned results will be filtered. For example:
let request = NSFetchRequest(entityName: "Agency")
request.predicate = // Your predicate...
let results = context.executeFetchRequest(request, error)
// Now do what you need with the results.
A final note, it's best not to force unwrap your results from executeRequest in case there is some problem and nil is returned - in that case your app would crash. You could instead use:
if let unwrappedResults = results {
// Now do what you want with the unwrapped results.
}
I suspect it has something to do with your use of SELF in the predicate format, and the "collection" referred to in the error message is the controller sub/class within which your code resides.
Try something like this (forgive me I'm Obj-C not Swift so may have the syntax incorrect).
let searchAttribute = <<entity.attribute key path>>
let result = NSPredicate(format:"%K CONTAINS[cd] %#",searchAttribute, searchText)
Where %K refers to the key path, that in the case of Core Data is your entity attribute. For example: Agency.name if that attribute exists for your Agency object.
Read about Predicate Format String Syntax.
UPDATE after third comment...
In my apps my solution includes the creation of a custom method in an extension of the Core Data generated NSManagedObject subclass. If that sounds like you know what I mean, let me know and I will post details.
In the meantime, create a custom method in whatever class your UISearchBar is controlled... (apologies Obj-C not Swift)
- (NSString *)searchKey {
NSString *tempSearchKey = nil;
NSString *searchAtrribute1 = Agency.attribute1;
NSString *searchAtrribute2 = Agency.attribute2;
NSString *searchAtrribute3 = Agency.attribute3;
NSString *searchAtrribute4 = Agency.attribute4;
tempSearchKey = [NSString stringWithFormat:#"%# %# %# %#", searchAtrribute1, searchAtrribute2, searchAtrribute3, searchAtrribute4];
return tempSearchKey;
}
You'll obviously need a strong reference for your Agency entity object to persist within the class, otherwise you will need to embed this bit of code into your searchBar function.
Work OK?