jazzedge
11/30/2017 - 12:17 PM

Swift - CloudKit - Get User ID

01. See:https://stackoverflow.com/questions/39980063/cloudkit-predicate

Problem:
I am making a messaging application and as push notification service I am using CloudKit. 
The push notifications work probably with the predicate 'TRUEPREDICATE', but the problem is if A sends a message to B, C gets a push notification too.My question now is how I can filter the predicate that only the person who gets a message gets a push notification. 

let database = CKContainer.default().publicCloudDatabase
let predicate = NSPredicate(format: "TRUEPREDICATE")
let subscription = CKQuerySubscription(recordType: "Message", predicate: predicate, options: .firesOnRecordCreation)
let notification = CKNotificationInfo()
notification.alertBody = "You have a new message!"
notification.soundName = "default"
subscription.notificationInfo = notification
database.save(subscription) { result, error in
   if let error = error {
       print(error.localizedDescription)
   }
}

ANSWER:

In your example a push notification would be triggered for each device the query subscription is setup on. If you want to correct this, you would need to modify your predicate to limit notifications to the current user/device.
For instance, if you are tracking a unique id for a specific user:

let userID = UUID().uuidString
let predicate = NSPredicate(format: "%K == %@", argumentArray: ["recipient", userID])

In this example, "recipient" is the CloudKit field name.



02. See: https://stackoverflow.com/questions/29315598/how-to-get-the-current-user-id-in-cloudkit

You'll want to call -[CKContainer fetchUserRecordIDWithCompletionHandler:] to fetch the current user record ID

Here is a snippet which I am always using to fetch the iPhone user’s iCloud ID (Apple calls it CloudKit Record ID) for an iOS app. The only thing you need to do in Xcode is to activate the "CloudKit" checkbox in your project’s iCloud capabilities. You do not need to actively use CloudKit at all - it is just the core of all iCloud activities since iOS 8.It is important to know that Apple never directly exposes the real iCloud ID but always just returns a secured hash of the iCloud ID and your app ID. But that should not worry you because that string is still unique for each user of your app across all his devices and can be used as login replacement.My function, is async and returns an optional CKRecordID object. The most interesting property of that CKRecordID object is recordName.CKRecordID.recordName is a 33 character string where the first character is always an underscore followed by 32 unique characters (== your user iCloud ID encoded for your app). It looks similar to: "_cd2f38d1db30d2fe80df12c89f463a9e"

import CloudKit

/// async gets iCloud record ID object of logged-in iCloud user
func iCloudUserIDAsync(complete: @escaping (_ instance: CKRecordID?, _ error: NSError?) -> ()) {
    let container = CKContainer.default()
    container.fetchUserRecordID() {
        recordID, error in
        if error != nil {
            print(error!.localizedDescription)
            complete(nil, error as NSError?)
        } else {
            print("fetched ID \(recordID?.recordName)")
            complete(recordID, nil)
        }
    }
}

// call the function above in the following way:
// (userID is the string you are interested in!)
iCloudUserIDAsync { (recordID: CKRecordID?, error: NSError?) in
    if let userID = recordID?.recordName {
        print("received iCloudID \(userID)")
    } else {
        print("Fetched iCloudID was nil")
    }
}


03. Use Predicate - How can an NSPredicate test that a value contains one of two other values?

See: https://stackoverflow.com/questions/47445895/how-can-an-nspredicate-test-that-a-value-contains-one-of-two-other-values

I'm trying to write an NSPredicate that expresses: "If uid contains myID OR targetUser"

If you'd wrote it in "code" and not in a predicate, you'd write if ([uid containsCD:myID] || (uid containsCD:targetUser]) not if ([uid containsCD:myID || targetUser]), where containsCD: is the equivalent of contains[cd]. Same logic for the predicate.
                                                                                    
Try:
NSPredicate *p3 = [NSPredicate predicateWithFormat:@"(uid contains[cd] %@) OR (uid contains[cd] %@)", myID, targetUser];

Or, without brackets try:
NSPredicate *predicate=[NSPredicate predicateWithFormat:@"uid contains[cd] %@ OR uid contains[cd] %@", myID, targetUser];