Search code examples
iosswiftcontacts

How to Determine if a Contact is Stored in iCloud, Google, or Another Account in Swift?


I am using CNContactStore to fetch contacts from an iOS device. While retrieving contacts, I want to check whether each contact is stored in iCloud, Google, Yahoo, or another account. However, I am only getting generic container names like "Card" or "Address Book" instead of the actual account names.

Here is my current code:

func fetchContacts(completion: @escaping ([ContactModel]) -> Void) {
    let store = CNContactStore()
    
    DispatchQueue.global(qos: .userInitiated).async {
        let keys: [CNKeyDescriptor] = [
            CNContactIdentifierKey as CNKeyDescriptor,
            CNContactGivenNameKey as CNKeyDescriptor,
            CNContactFamilyNameKey as CNKeyDescriptor,
            CNContactPhoneNumbersKey as CNKeyDescriptor
        ]
        
        var contacts = [ContactModel]()
        
        do {
            // Fetch all containers (iCloud, Google, Outlook, etc.)
            let containers = try store.containers(matching: nil)
            
            for container in containers {
                let request = CNContactFetchRequest(keysToFetch: keys)
                request.predicate = CNContact.predicateForContactsInContainer(withIdentifier: container.identifier)
                
                try store.enumerateContacts(with: request) { (contact, _) in
                    let fullName = "\(contact.givenName) \(contact.familyName)"
                    let phoneNumbers = contact.phoneNumbers.map { $0.value.stringValue }
                    
                    // Checking container type
                    print("Container Name: \(container.name), Type: \(container.type.rawValue)")

                    // Attempting to map account type
                    let accountName: String
                    switch container.type {
                    case .cardDAV:
                        accountName = container.name.contains("iCloud") ? "iCloud" : "Google/Yahoo (CardDAV)"
                    case .exchange:
                        accountName = "Outlook/Exchange"
                    case .local:
                        accountName = "Device Contacts"
                    default:
                        accountName = container.name
                    }
                    
                    let contactModel = ContactModel(contact: contact, accountName: accountName)
                    contacts.append(contactModel)
                }
            }
            
            DispatchQueue.main.async {
                completion(contacts)
            }
        } catch {
            print("Failed to fetch contacts: \(error)")
        }
    }
}

Using CNContainer.name → Returns "Card" or "Address Book", not "iCloud", "Google", etc.

How can I accurately determine whether a contact belongs to iCloud, Google, Yahoo, or another account?

current result


Solution

  • in Container object which we fetch we got this properties externalIdentifier=/carddav/v1/principals/garejakirit%40gmail.com/lists/default/

    externalModificationTag="6bae89da88eb93cc.701"

    externalSyncTag=https://www.googleapis.com/carddav/v1/synctoken/0802100118E4A18AF598E68B03220C08A4A886BE0610D8E2FA8701

    so using externalIdentifier we can get gmail or any other email and if iCloud than a 64 letter unique code

    and using this externalSyncTag we can get source if google than we got google api and if any other than that api so that's how we can determine the source of contact i did like this but is there any better way to do that please discuss here