Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CKContainer.discoverAllIdentities always fails

Tags:

swift

cloudkit

The CKContainer.discoverAllIdentities request always fails in my CloudKit app. It has continually failed over the course of several days.

A simplified version of the code that is failing (which results in the same error) is:

private func getContacts(completion: (([CKUserIdentity]?) -> Void)?) {
    container.status(forApplicationPermission: .userDiscoverability) { [weak self] status, error in
        if let error = error {
            print(error)
        }

        switch status {
        case .granted:
            self?.discover(completion: completion)
        default:
            print("status not granted")
        }
    }
}

private func discover(completion: (([CKUserIdentity]?) -> Void)?) {
    let op = CKDiscoverAllUserIdentitiesOperation()
    op.qualityOfService = .userInitiated
    op.discoverAllUserIdentitiesCompletionBlock = { error in
        if let error = error {
            print(error)
        }
    }
    op.userIdentityDiscoveredBlock = { identity in
        print(identity)
    }
    op.start()
}

It results in an error being passed to the op.discoverAllUserIdentitiesCompletionBlock. The description of the error in the log is:

<CKError 0x1c4a51a60: "Server Rejected Request" (15/2000); server message = "Internal server error"; uuid = F67453B9-712D-4E5E-9335-929123E3C978; container ID = "iCloud.com.huntermaximillionmonk.topdraw">

Previously, this operation would work, but only for certain iCloud users. Now it's not for both of my test users.

like image 341
Hunter Monk Avatar asked Aug 16 '17 00:08

Hunter Monk


2 Answers

Problem:

This was a problem in iOS 11.0

Based on my testing:

This works ok in Xcode 9.2 / iOS 11.2.1 on the device (not simulator)

After resetting the simulator works for the first time, doesn't work subsequently, however on the device it works repeatedly.

Code:

let queue = OperationQueue()

func requestPermissions(for permissions: CKApplicationPermissions,
                        completionHandler: @escaping (CKApplicationPermissionStatus, Error?) -> ()) {
    
    CKContainer.default().requestApplicationPermission(permissions) { status, error in
        
        if let error = error {
            
            print("Error for requesting \(permissions) - \(error)")
        }
        
        let statusMessage : String
        
        switch status {
            
        case .granted:
            statusMessage = "Granted"
        case .denied:
            statusMessage = "Denied"
        case .couldNotComplete:
            statusMessage = "Could not complete"
        case .initialState:
            statusMessage = "Initial state"
        }
        
        print("Permission - \(statusMessage)")
        
        completionHandler(status, error)
    }
}


private func discoverAllUsers() {
    
    let operation = CKDiscoverAllUserIdentitiesOperation()
    
    operation.userIdentityDiscoveredBlock = { userIdentity in
        
        print("userIdentity = \(userIdentity)")
    }
    
    operation.discoverAllUserIdentitiesCompletionBlock = { error in
        
        if let error = error {
            
            print("Discover all users Error: \(error) ")
        }
        else {
            print("Discover all users completed successfully")
        }
    }
    
    queue.addOperation(operation)
}
like image 127
user1046037 Avatar answered Oct 29 '22 02:10

user1046037


Edit:

Apple fixed this issue day after this answer was posted, coincidence?! I don't think so :)


This is not actually the answer to the question, but a fix that helped me to cross over this error. It will require you to change your app UI interaction and add ContactsUI framework to your project, moreover your user will be responsible for selecting a contact with iCloud related email.

Good news is that the method discoverUserIdentity is still works. So, you can use it to get CKUserIdentity from manually selected contact.

func addContact(_ contact:CNContact) {

     var lookUpEmails = [CKUserIdentityLookupInfo]()
     for email in contact.emailAddresses {
         lookUpEmails.append(CKUserIdentityLookupInfo(emailAddress: (email.value as String)))
     }

     let checkUserOperation = CKDiscoverUserIdentitiesOperation()
        
     checkUserOperation.userIdentityLookupInfos = lookUpEmails
        
     checkUserOperation.userIdentityDiscoveredBlock = { [unowned self] (identity, info) -> Void in
         if identity.hasiCloudAccount {
             if let recordID = identity.userRecordID {
                  //do something with discovered user
             }
             checkUserOperation.cancel()
         }
     }
     checkUserOperation.queuePriority = Operation.QueuePriority.high
     CKContainer.default().add(checkUserOperation)
}

It might sound useless, but in my case, it helped me to solve the Server Rejected Request" (15/2000) error, to fix one of the features of my app and continue to use the other feature related code with less efforts than I thought.

I hope someone will find this helpful.

like image 30
Dima Deplov Avatar answered Oct 29 '22 03:10

Dima Deplov