CloudKit: Not able to get the user's name through `CKContainer.default().shareParticipant(forUserRecordID: recordID)`

Hey everyone,

I'm trying to get the user's name to display in a welcome screen, but unfortunatelly no success so far.

For that, I'm using CKContainer.default().shareParticipant(forUserRecordID: recordID).userIdentity.nameComponents, but the returned nameComponents are empty, despite receiving no error and accountStatus of .available.

Here's my code:

struct Helper {
    static func getUserInformation() async throws -> Models.UserInfo {
        let container = CKContainer.default()

        let accountStatus = try! await container.accountStatus()
        var accountStatusDescription = ""

        switch accountStatus {
        case .couldNotDetermine:
            accountStatusDescription = "couldNotDetermine"
        case .available:
            accountStatusDescription = "available"
        case .restricted:
            accountStatusDescription = "restricted"
        case .noAccount:
            accountStatusDescription = "noAccount"
        case .temporarilyUnavailable:
            accountStatusDescription = "temporarilyUnavailable"
        @unknown default:
            accountStatusDescription = "default"
        }
        print("[Helper] CKContainer accountStatus: \(accountStatusDescription) ")
        // Prints "[Helper] CKContainer accountStatus: available"

        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 Models.ServiceError.userIdentityUnknownName
            }

            print("[Helper] CKShare.Participant nameComponents \(nameComponents)")
            // Prints "[Helper] CKShare.Participant nameComponents - "
            print("[Helper] CKShare nameComponents.givenName \(String(describing: nameComponents.givenName))")
            print("[Helper] CKShare nameComponents.nickname \(String(describing: nameComponents.nickname))")
            print("[Helper] CKShare nameComponents.familyName \(String(describing: nameComponents.familyName))")
            print("[Helper] CKShare nameComponents.namePrefix \(String(describing: nameComponents.namePrefix))")
            print("[Helper] CKShare nameComponents.nameSuffix \(String(describing: nameComponents.nameSuffix))")
            print("[Helper] CKShare nameComponents.middleName \(String(describing: nameComponents.middleName))")

            let name = PersonNameComponentsFormatter().string(from: nameComponents)
            return Models.UserInfo(
                id: id,
                name: name
            )
        } catch {
            throw error
        }
    }
}

Other than that, this project is using CloudKit for persistence through SwiftData and everything seems to be duly setup and working fine.

Any idea of what I might be missing? Any user permissions required? As far as I understood, from iOS 17 on and using this code, no permissions are required anymore but I may be wrong.

Any hint / help would be much appreciated!

Cheers,

Jorge

Replies

this project is using CloudKit for persistence through SwiftData...

In order to use CloudKit's sharing APIs, such as CKShare.Participant, which contains the info you'd like to access, you'd need to implement a coexistence approach with Core Data and SwiftData. In iOS 17 SwiftData can express only a subset of NSPersistentCloudKitContainer's capabilities, and CloudKit sharing isn't one of them.

That said, does your app have a use for CloudKit sharing? If not, and if your app is for macOS, you could use NSFullUserName().

In the event someone is using CloudKit sharing and needs to access the current user's name components, I found that using a CKShare directly works in iOS 17:

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

Thank you very much @ctrlswift! I currently do not have any use for CloudKit sharing in my project and it's only for iOS and watchOS, not macOS.

Trying to find an effective way to setup CoreData so that it coexists with SwiftData as suggested. If someone has any link that would point me in the right direction for doing so, I would be most grateful.

It's a bit of a bummer that that's the only way to do it though, as I do not need CoreData for any purpose at all other than getting the user name, since all the persistence is handled by SwiftData/CloudKit.

Cheers,

Jorge