URI typed attributes in CoreData: how to query with NSPredicate - swift

I have a CoreData-Entity that stores an attribute called "imageUrl" of type "URI".
It's used to store URL's (as in Swift URL / NSURL) for eg. rendering remote images.
How would I query for the string representation of an URI-type attribute?
Example: I'd like to get all objects that match "http://mydomain.jpg" or URL(string: "http://mydomain.jpg") to be more precise.
For attributes that are "String"-typed this would be sufficient:
NSPredicate(format: "myStringAttribute LIKE %#", "http://mydomain.jpg")
Would the following work for URI-type attributes?
NSPredicate(format: "imageUrl LIKE %#", URL(string: "http://mydomain.jpg"))

I'm answering to not let that question without an answer, but in my opinion, we three #nylki (the author), #Joakim Danielson and myself answered it together. I'll mark it as "Community Wiki" then.
URI in CoreData is a for URL objects. It's written as such in the documentation of NSAttributeDescription.AttributeType for the NSAttributeDescription.AttributeType.uri.
LIKE keyword in the predicate is for String comparison as stated by the Predicate Format String Syntax documentation, so we need to use = instead.
So the answer is:
NSPredicate(format: "imageUrl = %#", imageUrl as CVarArg)
Or
NSPredicate(format: "imageUrl = %#", argumentArray: [imageUrl])
if we don't want to use the as CVarArg.
A better way to avoid typo would be to use %K placeholder which is for %K is a var arg substitution for a key path.. Combined to #keyPath():
NSPredicate(format: "%K = %#", argumentArray: [#keyPath(YourEntityClass.imageUrl), imageUrl])
That way, if we wrote by error "imageUrI = %#" with an uppercase i instead of l that with might not see, we ensure the path.

Related

How to query CloudKit by lastModifiedUserRecordID?

Based on Apple documentation, lastModifiedUserRecordID, is a system field that's auto generated on iCloud.
I want to fetch records from the public iCloud database that where last modified by me.
I set "modifiedBy" as Queryable on the CloudKit Dashboard and added this predicate:
let userId = CKRecord.Reference(recordID: userCKRecordID, action: .none)
lastModifiedPred = NSPredicate(format: "lastModifiedUserRecordID = %#", userId)
And it returns no results. If I remove this predicate, it returns 1 result and I can see in the dashboard that the last modified by record id (recordName) matches with the user recordName I'm sending it.
Oddly enough, using "creatorUserRecordID" and passing the same value as above does return proper results but I need lastModifiedBy.
EDIT: Per the suggestions I tried to use the actual userCKRecord.recordName as a string and sending this predicate to CloudKit.
lastModifiedPred = NSPredicate(format: "lastModifiedUserRecordID = %#", userCKRecord.recordName)
and received this error:
"Field \'___modifiedBy\' has a value type of REFERENCE and cannot be
queried using filter value type STRING"
As I mentioned above, I can query creatorUserRecordID just fine using the original predicate at the top (using CKRecord.ID), but it just doesn't work for lastModifiedUserRecordID.
Since you want to fetch records that were last modified by you, you have to get first your UserRecordID. This is done by calling
fetchUserRecordID(completionHandler: { (myUserRecordID, error) in
… // see below
}
see the docs. This way, you get your user myUserRecordID.
With this, you can setup your query predicate:
let lastModifiedPred = NSPredicate(format: "lastModifiedUserRecordID = %#", myUserRecordID)
and execute your query.
You are using NSPredicate to compare a string literal. Where userId is not a string literal. Use this.
lastModifiedPred = NSPredicate(format: "lastModifiedUserRecordID = %d", INT-VALUE-OF-userId)
Based on apple documentation on https://developer.apple.com/documentation/cloudkit/ckrecord/id
CKRecord.ID -> class ID : NSObject
A record ID object consists of a name string and a zone ID. The name
string is an ASCII string not exceeding 255 characters in length.
comparing an object and string won't give you the result, try to cast the CKRecord.ID as String maybe help

core data predicate format in string swift4

I am using core data. For fetching data I want to give predicate as string. I have stored predicate in string attribute in database.
Below is code I am trying
let request = NSFetchRequest<Tasks>(entityName: "Tasks")
print("Query is .." ,item.query!)
request.predicate = NSPredicate(format: item.myqueryString!)
myqueryString is string attribute in core data where I am storing predicate as -
projectName == "spot"
But on fetching data it is giving error "NSInvalidArgumentException', reason: 'keypath spot not found in entity".
Anyone can explain what is wrong? Thanks in advance

Filter to NSFetchRequest

hi i'm not really understanding how the fetch filter works can anyone help me please? So i currently have this as my fetchall function which displays all of my items within my entity
im having trouble of filtering only one attribute which is a boolean. I want it to only display attributes that are true.
thankyou!
func fetchAllItems(){
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "AllItems")
do{
let result = try managedObjectContext.fetch(request)
beastList = result as! [AllItems]
} catch { print("\(error)")
}
}
Code:
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "AllItems")
request.predicate = NSPredicate(format: "something = %#", argumentArray: [true])
Note: Replace something with your boolean field name
Better way to create request
let request : NSFetchRequest<AllItems> = AllItems.fetchRequest()
Naming convention:
It is better to name your entity in singular form, so that each row in your entity could be in singular form.
AllItems seems very generic, if you are storing cars, the entity name could be Car. The variable that stores the result of the fetch request could be cars.
Reference:
https://developer.apple.com/documentation/foundation/nspredicate
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Predicates/Articles/pSyntax.html

coredata fetch records where the relation is not existing

I have an entity in swift with an optional relation towards another entity. Now I want to select those records without a relation towards the other entity.
I tried with a predicate (format: "relation = %#", nill) but this does not work.
how to fetch records without the relation filled?
Try this:
let fetchRequest: NSFetchRequest = YourEntity.fetchRequest()
let predicate = NSPredicate(format: "%K == nil", #keyPath(YourEntity.yourOptionalAttribute))
fetchRequest.predicate = predicate
// the rest of your code to perform the fetch goes here
#K is for keyPath. You just want to search where the keyPath is nil.

CloudKit Server Rejected Request: Unknown field recordName

I am doing a CKQuery like this in Swift 4:
CKQuery(recordType: package.recordType, predicate:
NSPredicate(format: "NOT (recordName IN %#)", package.recordNames as CVarArg))
...and I'm getting this CKError:
CKError(_nsError: CKError 0x60000024dce0: "Server Rejected Request" (15/2000); server message = "Unknown field 'recordName'"; uuid = BCD7C8DA-04B0-4422-8A24-F6479D234706; container ID = "...")
Is there a special key to use when querying against the recordName?
After digging some more, I discovered that you cannot query by the recordName directly, but you can query by the recordID. So instead of comparing an array of strings, you have to build an array of CKRecordIDs and query like this:
//- 1 - Build an array of CKRecordIDs
package.recordIDs = [CKRecordID(recordName: "..."), CKRecordID(recordName: "...")]
//- 2 - Set the predicate to use the `recordID` key
let pred = NSPredicate(format: "NOT (recordID IN %#)", package.recordIDs as CVarArg)
//- 3 - Set the query and drop your mic
CKQuery(recordType: package.recordType, predicate: pred)
I hope that helps someone else.