как получить все CNContactStore с устройства без фильтра

Я пытаюсь вставить в var contacts: [CNContact] = [] var store = CNContactStore(), но я не нашел правильный код для этой работы, я нашел эту функцию, которой мне нужно дать имя

func findContactsWithName(name: String) {
    AppDelegate.sharedDelegate().checkAccessStatus({ (accessGranted) -> Void in
        if accessGranted {
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                do {
                    let predicate: NSPredicate = CNContact.predicateForContactsMatchingName(name)
                    let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactBirthdayKey, CNContactViewController.descriptorForRequiredKeys()]
                    self.contacts = try self.store.unifiedContactsMatchingPredicate(predicate, keysToFetch:keysToFetch)


                    self.tableView.reloadData()
                }
                catch {
                    print("Unable to refetch the selected contact.")
                }
            })
        }
    })
}

Я хочу вставить self.contacts все записи, а не только одну с одинаковым именем


person shaharnakash    schedule 26.10.2015    source источник
comment
Привет, в приведенной ниже строке кода будет указано имя, фамилия, день рождения. ** let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactBirthdayKey, CNContactViewController.descriptorForRequiredKeys()]** Если я хочу получить все сведения о контакте, как получить?.   -  person Lakshmi Reddy    schedule 12.02.2017


Ответы (3)


Обновить

Основываясь на комментарии от OP, попробуйте следующий API на основе CNContactFetchRequest, чтобы получить все контакты без фильтра. Я запускаю это в фоновом потоке, чтобы уменьшить количество возможных проблем с огромным количеством контактов.

func findContactsOnBackgroundThread ( completionHandler:(contacts:[CNContact]?)->()) {

        dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { () -> Void in

            let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),CNContactPhoneNumbersKey] //CNContactIdentifierKey
            let fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch)
            var contacts = [CNContact]()
            CNContact.localizedStringForKey(CNLabelPhoneNumberiPhone)

            fetchRequest.mutableObjects = false
            fetchRequest.unifyResults = true
            fetchRequest.sortOrder = .UserDefault

            let contactStoreID = CNContactStore().defaultContainerIdentifier()
            print("\(contactStoreID)")


            do {

                try CNContactStore().enumerateContactsWithFetchRequest(fetchRequest) { (contact, stop) -> Void in
                    //do something with contact
                    if contact.phoneNumbers.count > 0 {
                        contacts.append(contact)
                    }

                }
            } catch let e as NSError {
                print(e.localizedDescription)
            }

            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                completionHandler(contacts: contacts)

            })
        })
    }

Вообще говоря, при использовании CNContactFetchRequest, а не так, как описано в вашем коде.

Примечание

Если вы хотите использовать существующий API, я рекомендую установить для предиката значение true:

 NSPredicate(value: true)

Это должно вернуть все контакты. Если это не сработает, рассмотрите возможность переключения на API CNConctactFetchRequest для перечисления контактов. В этом случае вы можете установить для предиката значение nil, чтобы получить все контакты (используя CNConctactFetchRequest).

CNContactFetch-предикат

Вот как вы можете изменить существующий метод:

func findContacts()->[CNContact] {
        AppDelegate.sharedDelegate().checkAccessStatus({ (accessGranted) -> Void in
            if accessGranted {
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    do {
                        let predicate: NSPredicate = NSPredicate(value: true)
                        let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactBirthdayKey, CNContactViewController.descriptorForRequiredKeys()]
                        self.contacts = try self.store.unifiedContactsMatchingPredicate(predicate, keysToFetch:keysToFetch)


                        self.tableView.reloadData()
                    }
                    catch {
                        print("Unable to refetch the selected contact.")
                    }
                })
            }
        })
    }

И использовать:

let contacts = findContacts()

У Apple есть более простой пример:

let store = CNContactStore()
let contacts = try store.unifiedContactsMatchingPredicate(CNContact.predicateForContactsMatchingName("Appleseed"), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey])

В вашем случае вы можете попробовать изменить образец Apple следующим образом:

//Use the reference to look up additional keys constants that you may want to fetch
let store = CNContactStore()
let contacts = try store.unifiedContactsMatchingPredicate(NSPredicate(value: true), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey])

Подробнее Образцы Apple для платформы контактов

person Tommie C.    schedule 26.10.2015
comment
Я получаю завершение приложения из-за необработанного исключения «CNInvalidPredicateException», причина: «Предикат TRUEPREDICATE не является допустимым предикатом контакта - person shaharnakash; 26.10.2015
comment
@shaharnakash — см. предоставленный образец CNContactFetchRequest. Пожалуйста, дайте мне знать, работает ли это в вашем случае? - person Tommie C.; 26.10.2015
comment
C теперь все работает, я использую, как вы предлагаете, пусть fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch) и теперь все отлично :) - person shaharnakash; 26.10.2015
comment
Эй, Томми! Я только что запустил этот код с помощью моего друга iPhone, и мы получили только около 10% для всех контактов. У вас есть идеи, что может вызвать это? Или как мы можем получить все списки контактов независимо от источника? - person Roi Mulia; 21.05.2018
comment
@RoiMulia - я не уверен, когда и где вы вызываете этот код, но просто имейте в виду, что вызов фоновых потоков в таких местах, как события окна, может вызвать проблемы с конкурирующими потоками, используемыми платформами Apple. Я бы попытался вызвать код из viewDidLoad или какого-либо действия, но имейте в виду, что многопоточность может привести к условиям гонки. Кроме того, убедитесь, что вы используете унифицированные настройки контактов... Могут быть и другие вещи, я не могу сказать без дополнительного контекста. Дайте мне знать, какие из этих предложений помогут в вашем случае. - person Tommie C.; 21.05.2018
comment
Привет, @TommieC. Спасибо, что так быстро ответили. Я запускаю код внутри функции viewDidLoad. Я прикладываю фрагмент кода, который использую: gist.github.com/roimulia/588be03804d9e97c0b157730e52bf516 Когда я запускаю код, он дает мне правильные результаты. Но на своем iPhone, я думаю, он входит в iCloud и Gmail. Возможно, это источник проблемы. Но я не могу найти способ понять, так как думаю, что мой код рекомендован Apple. Вы видите, что чего-то не хватает, чтобы получить ВСЕ контакты пользователя? - person Roi Mulia; 21.05.2018
comment
Должен ли я сохранить CNContactStore в глобальной памяти или сделать его общим? Есть ли в этом необходимость? - person Paresh. P; 30.12.2019
comment
@Paresh.P - Если вы имеете в виду, следует ли хранить переменную в глобальной области видимости или нет. Я бы поместил информацию только в пределах области, где это необходимо. Я бы, вероятно, сделал это прямо перед представлением, в котором создается список контактов. Потребуется больше контекста с образцом кода, чтобы принять лучшее решение. - person Tommie C.; 30.12.2019
comment
Большое спасибо за ваш ответ. Я просто просил сохранить память, которую получит любая глобальная переменная. - person Paresh. P; 31.12.2019

Измененный ответ Томми С для XCode 8 и Swift 3.0.

func findContactsOnBackgroundThread ( completionHandler:@escaping (_ contacts:[CNContact]?)->()) {

    DispatchQueue.global(qos: .userInitiated).async(execute: { () -> Void in

        let keysToFetch = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName),CNContactPhoneNumbersKey] as [Any] //CNContactIdentifierKey
        let fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch as! [CNKeyDescriptor])
        var contacts = [CNContact]()
        CNContact.localizedString(forKey: CNLabelPhoneNumberiPhone)

        if #available(iOS 10.0, *) {
            fetchRequest.mutableObjects = false
        } else {
            // Fallback on earlier versions
        }
        fetchRequest.unifyResults = true
        fetchRequest.sortOrder = .userDefault

        let contactStoreID = CNContactStore().defaultContainerIdentifier()
        print("\(contactStoreID)")


        do {

            try CNContactStore().enumerateContacts(with: fetchRequest) { (contact, stop) -> Void in
                //do something with contact
                if contact.phoneNumbers.count > 0 {
                    contacts.append(contact)
                }

            }
        } catch let e as NSError {
            print(e.localizedDescription)
        }

        DispatchQueue.main.async(execute: { () -> Void in
            completionHandler(contacts)

        })
    })
} 

override func viewDidLoad() {
    findContactsOnBackgroundThread { (contacts) in
            self.contactsList = contacts
            self.tableView.reloadData()
        }
}
person thefinnomenon    schedule 01.10.2016

/// The default key descriptors to fetch
public var defaultFetchKeys: [String] = [CNContactGivenNameKey,
                                         CNContactFamilyNameKey,
                                         CNContactPhoneNumbersKey,
                                         CNContactImageDataAvailableKey,
                                         CNContactThumbnailImageDataKey]

/**
     Fetch contacts from the user's device

     - parameter sortOrder: The sort order that should be used. Default none.
     - returns: A list of contacts
     */
    public func fetchContacts(withSortOrder sortOrder: CNContactSortOrder = .givenName,
                              qos: DispatchQoS.QoSClass = .background) -> Promise<[Contact]>
    {

        return Promise { seal in

            DispatchQueue.global(qos: qos).async {

                let store = CNContactStore()
                var contacts = [Contact]()

                /// A `PhoneNumberKit` object used to parse and format numbers (expensive!)
                let phoneNumberKit = PhoneNumberKit()

                let request = CNContactFetchRequest(keysToFetch: self.defaultFetchKeys as [CNKeyDescriptor])
                request.sortOrder = sortOrder
                request.unifyResults = true

                do {

                    try store.enumerateContacts(with: request) { contact, _ in

                        if let user = Contact(contact: contact, kit: phoneNumberKit) {
                            contacts.append(user)
                        }

                    }

                    DispatchQueue.main.async {
                        seal.fulfill(contacts)
                    }

                } catch {

                    DispatchQueue.main.async {
                        seal.reject(error)
                    }

                }

            }

        }

    }

Я также использую PhoneNumberKit для стандартизации всех чисел и PromiseKit для цепочки запросов, но вы можете настроить его по своему усмотрению.

person Danut Pralea    schedule 03.04.2019