Unable to fetch from coreData using Predicate as String? - ios

I am unable to fetch data using a string as a direct argument to NSPredicate. Is there any difference between these two,
//Working case
fetchRequest.predicate = NSPredicate(format: "role == %#","GK")
In the above case, I am able to fetch the data with the predicate.
//Not Working
predicateString = String(format:"role == %#","GK")
fetchRequest.predicate = NSPredicate(format: predicateString)
Here I am unable to fetch the data.
The only difference between the above two cases is that I'm using a String variable to define the predicate. What is wrong here?

When the %# is replaced with a string variable when instantiating a NSPredicate the string variable is automatically surrounded by quotes so in your example the predicate becomes role == 'GK'
If you want to use String(format:...) you need to add the quotes yourself but in my opinion it's better to use NSPredicate(format:...) directly instead to avoid issues like this.

Format of NSPredicate is as following, where it expects a format, and format contains equaliser.
NSPredicate(format: <String>, <args: CVarArg...>)
let predicate1 = NSPredicate(format: "role == %#", "GK")
Here, if you will check the predicate1.predicateFormat, then you will get:
"role == \"GK\"",
which equilise role with string "GK" and return result in array.
But for,
let predicateString = String(format:"role == %#","GK")
let predicate2 = NSPredicate(format: predicateString)
You replaced the equaliser with simple string, which fails to return anything. You can check with predicate1.predicateFormat which will return:
"role == GK"
Here you can use "role == 'GK'" if you want to use string.
Hope it will help.

Related

Generics method with predicated core data search won’t return correct result

I’m trying to have a generic method that search entities for a chosen string inside an attribute to see how many such entities are there, which then will decide what number or lack of number to attach to the string as it was returned. I’ve shown it in the picture. The entity type, the search string, and the attribute are all determined by input parameters. The problem is I cannot seem to make this generic method work consistently. On some projects it works, on others it doesn’t and the predicated search either returns an empty string or incomplete result. Can anyone help me figure it out? I’ve been scratching my head since last month.
Code:
private func getDefaultNameFor<T>Centityt T, defaultString: String, attribute: String)—>String{
var count = 0
var defaultName = defaultString
let type = T.self
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: “\(type)”)
let pred = NSPredicate(format: “%# CONTAINS[cd] %#", attribute, defaultString)
fetchRequest.predicate = pred
var untitledEntities: [NSManagedObject] = []
do {
untitledEntities = try context.fetch(fetchRequest)
}catch{
print("error fetching existing default names: \(error)”
}
count = untitledEntities.count
print("pred rEturns: " untitledEntities.description)
if count !=0 {
defaultName = defaultString + String(count)
}
print("default name: " defaultName)
return defaultName
}
Keypaths are substituted with %K instead of %#. (format: “%K CONTAINS[cd] %#", attribute, defaultString) worked.
Nothing wrong with using the generic, although I think not using it might still be preferable. This was a very basic sort of error I hope I had caught earlier by reviewing the predicate formatting guide.
Here’s how I solved it:
1: as suggested, I changed it from generic to taking a simple string as entity name, but the search still returns empty. I checked again at the pred but I don’t think it was formatted wrong. Am I missing something?
2: okay so I just adjusted the pred to be a simple format: “name CONTAINS[cd] ‘untitled’” and it worked fine. So why is it not working in (format: “%# CONTAINS[cd] %#", attribute, defaultString) ? I even tried to add ‘’ around the second %# but result was still empty.
3: (format: “name CONTAINS[cd] %#", defaultString) is working fine. So I guess the problem is the first substitute not functioning as keypath in the predicate format. So how to make it function as so?
4: Solved it! Keypaths are substituted with %K instead of %#. (format: “%K CONTAINS[cd] %#", attribute, defaultString) worked. This is such a basic error. I cannot believe I’ve been bugged with this for a month almost.

CoreData fetch request NSPredicate not working

I've added a new bool property to an entity in my CoreData model and migrated the existing data to the new model. I now want to fetch only the data with a bool value of false. I tried everything suggested here on StackOverflow but I could not find an answer to my problem.
I tried following predicates:
let predicate = NSPredicate(format: "%K == %i", #keyPath(Tag.isWrite), false)
fetchRequest.predicate = predicate
let predicate = NSPredicate(format: "isWrite == 0")
fetchRequest.predicate = predicate
let predicate = NSPredicate(format: "isWrite == %#", NSNumber(value: false))
fetchRequest.predicate = predicate
The default value for the migrated model is false for isWrite. When I don't apply the predicate and set a breakpoint before displaying my data I can clearly see that isWrite is false in the debugger. When I apply the predicate I don't get any data.
Am I missing something out?
There appears to be a problem in CoreData with attribute names beginning with “is...”. Try changing the name for your attribute.

Parsing UUID in NSPredicate

I am trying to get data from the Realm database. I am using NSPredicate. And it was working well. But today I have to get data from Object who has string Id. This Id is in UUID. So when ever I try to get the value using UUID(the String ID), it gives me error like so
nable to parse the format string "Id == BD1698EE-C57D-4B8D-9D54-1D4403B2136F"'
This is the error statement. Whereas I have the following line in the code.
let resultPredicateShoppingListDetail = NSPredicate(format: "Id == \(shoppingListModel.Id)")
It does not make sense to me. Why this is happening?
Don't use string interpolation to build a predicate format, it is very
difficult to get the necessary quoting correct. As an example, this would work (note the additional
single quotes):
let uuid = "BD1698EE-C57D-4B8D-9D54-1D4403B2136F"
print(NSPredicate(format: "id == '\(uuid)'"))
// id == "BD1698EE-C57D-4B8D-9D54-1D4403B2136F"
but also fail if the uuid string contains a single quote.
Better use the %# var arg substitution for strings:
let uuid = "BD1698EE-C57D-4B8D-9D54-1D4403B2136F"
print(NSPredicate(format: "id == %#", uuid))
// id == "BD1698EE-C57D-4B8D-9D54-1D4403B2136F"
In your case (assuming that shoppingListModel.Id is a String or NSString):
let resultPredicateShoppingListDetail = NSPredicate(format: "Id == %#", shoppingListModel.Id)
Even better, use the %K keypath var arg substitution and the #keyPath
compiler directive to insert the correct key path:
let resultPredicateShoppingListDetail = NSPredicate(format: "%K == %#",
#keyPath(ShoppingList.Id), shoppingListModel.Id)
For more information about %K and %# in predicates, see
“Predicate Format String Syntax” in the “Predicate Programming Guide.”

NSPredicate matching string for Cloudkit

I need to pull the record from cloudkit matching a string.
I have a User record type, with an email field. I have multiple records with the same email, but I can't get the predicate to get me the records.
I've tried all of these:
let predicate = NSPredicate(format: "email = 'julio_ukohgsp_chevez#tfbnw.net'")
NSPredicate(format: "email == %#", argumentArray: [email])
NSPredicate(format: "email IN %#", [email])
NSPredicate(format: "email contains %#", email)
NSPredicate(format: "email = %#", email)
NSPredicate(format: "email == %#", email)
My query specifies the record type:
let query = CKQuery(recordType: "User", predicate: NSPredicate(format: "email == %#", email))
When I do a predicate with value: true, I get all records including the one I want.. I know for sure I have a User with that email, multiple in fact..
What am I missing?
Edit ..
let query = CKQuery(recordType: "User", predicate: NSPredicate(format: "email BEGINSWITH %#", email))
Does work and brings back records, but I want an exact match!
I had same issue and was able to resolve it by adding Queryable index on user field in my case. Note, after index was added I needed to write data again to CloudKit to be able to perform query.
Code for creation of predicate:
let user = "test-user"
let userPredicate = NSPredicate(format: "user == %#", user)
I've been having this same issue with one of my apps. In my case, I needed to subscribe to a list of predicates where a lot of search criteria needed to be met but the search or filter predicate gave me the most trouble. I also tried:
let word1 = "Mac"
let predicate = NSPredicate(format: "Title CONTAINS %#", word1)
This worked but only when 'word1' was a single word with no spaces and 'Title' was a 'String List' in the cloud.
The clarification that I have come across in all my research is:
You need to use the term 'self' when you create a predicate and what that does is it searches all the fields that are a searchable string. Here is the information I got from Apple's Website.
Predicates support the following variable substitution strings:
Use %# for value objects such as strings, numbers, and dates.
Use %K for the name of a field. This substitution variable indicates that the substituted string should be used to look up a field name.
With one exception, the CONTAINS operator can be used only to test list membership. The exception is when you use it to perform full-text searches in conjunction with the self key path. The self key path causes the server to look in searchable string-based fields for the specified token string. For example, a predicate string of #"self contains 'blue'" searches for the word “blue” in all fields marked for inclusion in full-text searches. You cannot use the self key path to search in fields whose type is not a string.
What I ended up doing, which there has to be a better way, is my 'Title' property is still a 'String List' in the cloud and in my code I made an array of all the words in my searchTerm and with each word in the searchTerm I made a separate NSPredicate and I used those as part of a NSCompoundPredicate. I then added the NSCompoundPredicate to my subscription and it worked. Here is my example:
var searchCriteria: [NSPredicate] = []
for word in searchTerm {
let predicate = NSPredicate(format: "self CONTAINS %#", word)
searchCriteria.append(predicate)
}
let p2 = NSPredicate(format: "Price > %d", minPrice)
let p3 = NSPredicate(format: "Price < %d", maxPrice)
let p4 = NSPredicate(format: "Category == %d", category)
searchCriteria.append(p2)
searchCriteria.append(p3)
searchCriteria.append(p4)
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: searchCriteria)
I hope this helps. There has to be a better way to do this and if anyone knows, please let me know cause I understand there isn't a lot of good explanations on how to do things with CloudKit.

Realm subquery with List count in Swift

I have
class Car : Object
{
let queries : List<QueryModel> = List<QueryModel>()
}
and
class QueryModel : Object
{
let location : String = ""
}
Now how do I write that NSPredicate to fetch just that cars which have queries with location equal to 'London'?
p.s. It's a known fact that things like #count do not work on Swift List class...
p.p.s. I need it as a predicate because this is the only way RBQFetchedResultsController knows how to do it's job.
How I thought it could've been written was something like NSPredicate(format: "SUBQUERY(queries, $query, $query.location == %#)", 'London'). But nothing like this ever passes Realm's predicate validation :)
You can write
NSPredicate(format: "ANY queries.location = %#", locationString)
If you want to add other filtering, you can create other NSPredicate and then use
NSCompoundPredicate(andPredicateWithSubpredicates: [pred1, pred2])
to combine the filters.
Now, if you want to filter cars, you have to prepare a
let request = FetchRequest<Car>(realm: realm, predicate: predicate)
then you can use RBQFetchedResultsController to execute the request.

Resources