How to fetch User first and last name?

362 views Asked by At

I attempted to retrieve the first name and last name of the current user from CloudKit. However, I received a message from Xcode indicating that the 'userDiscoverability' feature will be deprecated in iOS 17.0. This feature is no longer supported, and instead, I should consider using the "Sharing CloudKit Data with Other iCloud Users" feature.

Here's the function I've been using:

 func fetchUserName(completion: @escaping ( _ fullName:String, _ success:Bool) -> Void) {
        container.requestApplicationPermission(.userDiscoverability) { (status, error) in
            self.container.fetchUserRecordID { (record, error) in
                self.container.discoverUserIdentity(withUserRecordID: record!) { (userID, error) in
                    var name: String = ""
        
                    self.container.accountStatus { accountStatus, error in
                        if let error = error {
                            print(error.localizedDescription)
                        }
                        
                    }

                    guard let givenName = userID?.nameComponents?.givenName,
                          let familyName = userID?.nameComponents?.familyName else {
                        completion("", false)
                        print("Unable to fetch user name")
                        return
                    }
                    
                    let fullName: String = givenName + " " + familyName
                    name = fullName

                    DispatchQueue.main.async {
                        completion(name, true)
                    }
                }
            }
        }
    }

Does anyone have a solution on how to resolve this issue?

3

There are 3 answers

2
Alex Ioja-Yang On BEST ANSWER

You no longer need the outer call (container.requestApplicationPermission).

In order to retrieve the name, you'll need to first get active user record ID, then fetch the share participant, using the ID, and finally retrieve the name from the CKShare.Participant object. Your code already does this, and a modifier version of it would look like:

 func fetchUserName(completion: @escaping ( _ fullName:String, _ success:Bool) -> Void) {
        self.container.fetchUserRecordID { (record, error) in
            self.container.fetchShareParticipant(withUserRecordID: record!) { (userID, error) in
                var name: String = ""
        
                self.container.accountStatus { accountStatus, error in
                    if let error = error {
                        print(error.localizedDescription)
                    }
                        
                }

                guard let givenName = userID?.nameComponents?.givenName,
                      let familyName = userID?.nameComponents?.familyName else {
                    completion("", false)
                    print("Unable to fetch user name")
                    return
                }
                    
                let fullName: String = givenName + " " + familyName
                name = fullName

                DispatchQueue.main.async {
                    completion(name, true)
                }
            }
        }
    }

If not already enabled, you will also need to enable CloudKit in your app.

1
foolbear On

await version:

static func getUserInformation() async throws -> (id: String, name: String) {
    do {
        let recordID = try await container.userRecordID()
        let id = recordID.recordName
        
        let participant = try await container.shareParticipant(forUserRecordID: recordID)
        guard let nameComponents = participant.userIdentity.nameComponents else {
            throw CloudKitError.userIdentityUnknownName
        }
        let name = PersonNameComponentsFormatter().string(from: nameComponents)

        return (id, name)
    } catch {
        throw error
    }
}
0
ctrlswift On

In both of the proposed solutions above, userIdentity.nameComponents failed to return a value for me, but I found using a CKShare directly works:

func fetchCurrentUserName(for share: CKShare) -> String {
    var name = ""
    let currentUser = share.currentUserParticipant
    if let nameComponents = currentUser?.userIdentity.nameComponents {
      name = nameComponents.formatted()
    }
    return name
}

Investigating Apple's sample project Sharing Core Data objects between iCloud users led me to this solution. It has a ton of useful information and hints.