Search code examples
iosswiftnspredicatecloudkit

IOS - Swift: OrPredicate in CloudKit results in invalid


I have this bit to retrieve all messages between two users:

let user1 = "john"
let user2 = "mark"
let predicateUser1To2 = NSPredicate(format: "user1 == %@", user1)
let predicateUser2From1 = NSPredicate(format: "user2 == %@", user2)

let predicateUser2To1 = NSPredicate(format: "user2 == %@", user1)
let predicateUser1From2 = NSPredicate(format: "user1 == %@", user2)

let predicate1To2 = NSCompoundPredicate(andPredicateWithSubpredicates: [predicateUser1To2, predicateUser2From1])
let predicate2To1 = NSCompoundPredicate(andPredicateWithSubpredicates: [predicateUser1From2, predicateUser2To1])
let predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [predicate1To2, predicate2To1])

let query = CKQuery(recordType: "Chat", predicate: predicate)

But it throws the error below. It seems it got evaluated correctly, why is it wrong!?

*** Terminating app due to uncaught exception 'CKException', reason: 'Invalid predicate: (user1 == "john" AND user2 == "mark") OR (user1 == "mark" AND user2 == "john") (Error Domain=CKErrorDomain Code=12 "(user1 == "john" AND user2 == "mark") OR (user1 == "mark" AND user2 == "john") is not a comparison predicate" UserInfo={NSLocalizedDescription=(user1 == "john" AND user2 == "mark") OR (user1 == "mark" AND user2 == "john") is not a comparison predicate, ck_isComparisonError=true})'


Solution

  • OR queries are not supported by CloudKit. For more information see: https://developer.apple.com/library/ios/documentation/CloudKit/Reference/CKQuery_class/

    In a lot of cases you could solve this by using an in predicate but that will only work on one field. In your case you might consider a predicate like:

    NSPredicate(format: "user1 in %@ AND user2 in %@", ["john", "mark"], ["john", "mark"])
    

    This one is still not exactly the same as your sample. It could be that user1 is the same as user2. For instance both could be mark. In your case that record is not selected and in the sample above it's selected. You could add an extra AND condition to exclude those. You could add: AND user1 != user2

    If a query gets more complex and an in query is not possible, then you could execute 2 queries and merge the result.