CloudKit Server Rejected Request: Unknown field recordName - cloudkit

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.

Related

URI typed attributes in CoreData: how to query with NSPredicate

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.

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

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.

Realm object predicate search is invalid

Using Realm DB in a swift application. I'm trying to filter the results with a predicate as follows:
class func fetchUsersFromDB(usersId: [String]) -> [User]{
var users = [User]()
let realm = Realm()
let predicate = NSPredicate(format: "objectId IN %#", argumentArray: usersId)
var allUsers = realm.objects(User).filter(predicate)
users = Array(allUsers)
return users
}
But this won't compile. I get this error:
Terminating app due to uncaught exception 'Invalid value', reason: 'IN clause requires an array of items'
Any ideas what I'm doing wrong?
Remove the argumentArray: label, as with it you're calling the wrong initializer for NSPredicate:
let predicate = NSPredicate(format: "objectId IN %#", usersId)
As of Swift 3, just use Array(usersId) instead of usersId.

Can I apply multiple predicates to an NSFetchRequest? Would it be better to manually parse my results?

Ok I have a basic iPad app that asks for 5 search/filter criteria from the user. Based on this data, I need to go to my core data db, and pull out any managed objects that fit that criteria. It seems like I need to apply more than one predicate to the same request, is that possible? Or could I just write a really long fancy predicate? With multiple requirements? How should I approach that?
Would it be a good idea to just grab all the entities through the fetch request, and then loop through each array and grab any objects that I find that fit my search criteria?
Please advise!
Yes it's possible. You're looking for compound predicates and here's an example with AND predicates:
NSPredicate *compoundPredicate
= [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray of Predicates]];
You can also use notPredicateWithSubpredicates and orPredicateWithSubpredicates depending on your needs.
Link to documentation https://developer.apple.com/documentation/foundation/nscompoundpredicate
Swift 4
let fetchRequest: NSFetchRequest<YourModelEntityName> = YourModelEntityName.fetchRequest()
let fooValue = "foo"
let barValue = "bar"
let firstAttributePredicate = NSPredicate(format: "firstAttribute = %#", fooValue as CVarArg)
let secondAttributePredicate = NSPredicate(format: "secondAttribute = %#", barValue as CVarArg)
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [firstAttributePredicate, secondAttributePredicate])
more information about different types of NSCompoundPredicate constructors can be found here
yes you can apply like this below
do {
let request = UserDeatils.fetchRequest() as NSFetchRequest<UserDeatils>
let firstAttributePredicate = NSPredicate(format: "userName = %#", userNameTextField.text!)
let secondAttributePredicate = NSPredicate(format: "password = %#", passwordTF.text!)
request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [firstAttributePredicate, secondAttributePredicate])
self.userDataArray = try context.fetch(request)
print("Fetching Data", userDataArray)
if userDataArray.isEmpty {
print("no user found")
} else {
print("Login Succesfully")
}
} catch {
print("Wrong Username and password")
}