Reputation: 4300
I have a CloudKit
app where records are stored locally in CoreData
. When creating the CoreData
record, a UUID
is generated and populated to the system recordName field. The app creates and saves records in CoreData
then uploads the records to CloudKit
. This works fine.
However, I am now coding to modify a record and am not able to fetch a record by the recordName UUID. I have not been successful setting a predicate to search for only that record. I can retrieve all records with TRUEPREDICATE
and I can also retrieve a single record from a field I created in the CloudKit
record type. I created a field called myRecordName where I store the same UUID as the CloudKit
recordName. A query using the myRecordName works fine.
I have shown three methods below for the predicates. If p1 and p2 are not both commented out the app crashes on running. I assume I am missing something really simple here. Any guidance would be appreciated. Xcode 10.2.1, iOS 12.2, testing on a real device.
If either p1 or p2 or both are not commented out, the console shows:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CKReference rangeOfString:]: unrecognized selector sent to instance 0x280878520'
func queryDatabase() {
let recordZone = CKRecordZone(zoneName: "PrimaryZone")
let recName = "0E763775-5AD3-4A50-BFB8-AC310180E8A2"
let recID = CKRecord.ID(recordName: recName, zoneID: recordZone.zoneID)
let searchRecord = CKRecord(recordType: "Patient", recordID: recID)
let p1 = NSPredicate(format: "%K == %@", CKRecord.Reference(recordID: recID, action: CKRecord_Reference_Action.none))
let p2 = NSPredicate(format: "%K == %@", CKRecord.Reference(record: searchRecord, action: CKRecord_Reference_Action.none))
//let predicate = NSPredicate(format: "TRUEPREDICATE")
let p3 = NSPredicate(format: "myRecordName = %@", recName)
let query = CKQuery(recordType: "Patient", predicate: p3)
let sortDescriptor = NSSortDescriptor(key: "lastNameText", ascending: true)
query.sortDescriptors = [sortDescriptor]
privateDatabase.perform(query, inZoneWith: recordZone.zoneID) { (results, error) in
if error != nil {
print("error querying database\(String(describing: error?.localizedDescription))")
} else {
DVC.ckRecords = results!
//print(results!)
print(DVC.ckRecords.first!)
}//if error else
}//perform query block
}//queryDatabase
Upvotes: 0
Views: 797
Reputation: 5020
I've never used CloudKit, so maybe I'm wrong, but…
The two problem lines of code, p1=
and p2=
, attempt to create a NSPredicate
using a format string. Each format string contains two var arg substitutions, aka placeholders: %K
and %@
. So for this to work, that format string argument must be followed by two more arguments: first a key path, which should be a string (%K), and second a object for its value (%@). For your purposes here, the key path is probably just an attribute name.
But you have only provided one more argument, a CKRecord.Reference
object, which the system tries to parse for the first (attribute name, string) argument. The error you are getting is typical of what happens when the system tries to parse an object such as CKRecord.Reference
when it was expecting a string. Swift may be quite type safe but old-fashioned NSPredicate var args functions are not :)
To fix this problem (and move on to the next one), you should provide the key path (argument name) argument, something like this:
let p1 = NSPredicate(format: "%K == %@", "recordID", CKRecord.Reference(recordID: recID, action: CKRecord_Reference_Action.none))
let p2 = NSPredicate(format: "%K == %@", "recordID", CKRecord.Reference(record: searchRecord, action: CKRecord_Reference_Action.none))
Since I don't understand exactly what you are doing, you may need to tweak those two lines a bit. But the point is that, given your format string, you need three arguments to NSPredicate(format:)
, and the middle one needs to be a string representing a key path or attribute name.
Upvotes: 1