CNContactStore NSPredicate дано 2 имени контакта в одной строке

Я работаю над приложением, которое работает с внешним устройством ANCS. Короче говоря, устройство определяет, когда приходит SMS, и сообщает об этом приложению. Затем приложение сможет отправить сообщение обратно отправителю SMS через веб-сервис (Twilio).

Когда человек, отправивший смс, находится в Контактах, то ANCS-устройство видит только имя этого контакта, а не сам номер телефона. Таким образом, он предоставляет только это читаемое имя для приложения. Если у контакта есть только одно имя (например, «Джон»), поиск контакта и его телефонных номеров выполняется довольно просто, например:

let title = "John"
let contactStore = CNContactStore()
let predicate = CNContact.predicateForContacts(matchingName: title)
let keys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactMiddleNameKey, CNContactNicknameKey, CNContactOrganizationNameKey, CNContactPhoneNumbersKey]
var contacts = [CNContact]()
do {
    contacts = try contactStore.unifiedContacts(matching: predicate, keysToFetch: keys as [CNKeyDescriptor])
    if contacts.count == 0 {
        print("No contacts were found matching the given name.")
    }
    else
    {
        print("found \(contacts.count) matches !")
        // from there find the best possible bet and the numbers for this contact
    }
} catch {
    print("Unable to fetch contacts.")
}

Но иногда у людей хранится несколько контактов для одного и того же человека с разными именами, например, когда приходит SMS, в нем может быть написано, что оно пришло от: «Джон или папа» — потому что оно соответствует этим 2 контактам в адресной книге. (нам всем приходится иметь дело с такими дубликатами в наших адресных книгах). А иногда в худшем случае может быть 3 и более имен!

Вот к чему я хочу прийти: как мы можем различать/создавать предикат для проверки этих контактов. Самым простым решением было бы разбить имя на слово «или», но что, если это корейское, немецкое, шведское и т. д. слово? А что, если это слово принадлежит одному Контакту, имя которого, скажем, "Джон стол на крыше"? В худшем случае iOS предоставит локализованное слово-разделитель «или»?

Кто-нибудь сталкивался с такой проблемой?


person JBA    schedule 18.10.2016    source источник


Ответы (1)


Наконец-то мне удалось сделать так:

  • Во-первых, я получил список всех переведенных или слов, предоставленных Apple по этому адресу, используя для каждого языка слово из файла TelephonyUtilities.lg

  • Во-вторых, я создал файл plist, содержащий все эти слова для каждого предпочтительного языкового ключа (en, es и т. д.).

  • В-третьих, когда у меня есть имя контакта, я беру правильную строку слов с учетом предпочтительного языка телефона и пытаюсь разделить ее... Если найдено более 1 результата разделения, это означает, что это, скорее всего, два имени контакта для один и тот же контакт, поэтому я извлекаю эти контакты, чтобы найти общий номер телефона. И это работает довольно хорошо до сих пор.

Несколько примеров кода для иллюстрации (swift 3):

Как выглядит файл plist (заполните, кому может помочь):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>en</key>
<string> or </string>
<key>fr</key>
<string> ou </string>
<key>vi</key>
<string> hoặc </string>
<key>hr</key>
<string> ili </string>
<key>hu</key>
<string> vagy </string>
<key>id</key>
<string> atau </string>
<key>it</key>
<string> o </string>
<key>es</key>
<string> o </string>
<key>ar</key>
<string> أو </string>
<key>zh-CN</key>
<string>还是</string>
<key>zh-TW</key>
<string> 或 </string>
<key>sk</key>
<string> alebo </string>
<key>pt</key>
<string> ou </string>
<key>pl</key>
<string> lub </string>
<key>cs</key>
<string> nebo </string>
<key>ca</key>
<string> o </string>
<key>nl</key>
<string> of </string>
<key>fi</key>
<string> tai </string>
<key>da</key>
<string> eller </string>
<key>de</key>
<string> oder </string>
<key>el</key>
<string> ή </string>
<key>he</key>
<string> או </string>
<key>ja</key>
<string>または</string>
<key>ko</key>
<string> 또는 </string>
<key>ms</key>
<string> atau </string>
<key>no</key>
<string> eller </string>
<key>th</key>
<string> หรือ </string>
<key>uk</key>
<string> чи </string>
<key>tr</key>
<string> veya </string>
<key>ro</key>
<string> sau </string>
<key>ru</key>
<string> или </string>
<key>sv</key>
<string> eller </string>
</dict>
</plist>

Получите или слово String:

private func getOrWord() -> String
{
    var orWord:String = ""

    var languageCode:String = ""
    if (NSLocale.current.languageCode != nil) { languageCode = NSLocale.current.languageCode! }
    var countryCode:String = ""
    if (NSLocale.current.regionCode != nil) { countryCode = NSLocale.current.regionCode! }
    let bothCodes:String = "\(languageCode)-\(countryCode)"

    var propertyListForamt =  PropertyListSerialization.PropertyListFormat.xml //Format of the Property List.
    var plistData: [String: AnyObject] = [:] 
    let plistPath: String? = Bundle.main.path(forResource: "separator", ofType: "plist")! //the path of the data
    let plistXML = FileManager.default.contents(atPath: plistPath!)!
    do { 
        plistData = try PropertyListSerialization.propertyList(from: plistXML, options: .mutableContainersAndLeaves, format: &propertyListForamt) as! [String:AnyObject]

        if (languageCode != "")
        {
            if (plistData[languageCode] != nil)
            {
                orWord = plistData[languageCode] as! String
            }
            else if (plistData[bothCodes] != nil)
            {
                orWord = plistData[bothCodes] as! String
            }
        }

    } catch {
        print("Error reading plist: \(error), format: \(propertyListForamt)")
    }

    return orWord
}

И, наконец, получить возможные контакты:

private func searchForContacts(name:String) -> Array<CNContact>
{
    var namesToSearch:Array<String> = []
    var contactsFound:Array<CNContact> = []

    let orWord:String = getOrWord()

    if (orWord == "")
    {
        namesToSearch.append(name)
    }
    else
    {
        namesToSearch = name.components(separatedBy: orWord)
    }

    if (namesToSearch.count > 0)
    {
        let contactStore = CNContactStore()

        for nameSearch in namesToSearch
        {
            let predicateName = CNContact.predicateForContacts(matchingName: nameSearch)

            let keys = [CNContactPhoneNumbersKey]
            var contacts = [CNContact]()
            do {
                contacts = try contactStore.unifiedContacts(matching: predicateName, keysToFetch: keys as [CNKeyDescriptor])
                if (contacts.count > 0)
                {
                    // keep only the ones that have at least 1 phone number
                    for contact in contacts
                    {
                        if (contact.phoneNumbers.count > 0)
                        {
                            contactsFound.append(contact)
                        }
                    }

                }
            } catch {
                // not possible to fetch
            }
        }
    }

    return contactsFound
}
person JBA    schedule 23.10.2016
comment
Привет, у меня такая же проблема, но я пытаюсь создать свой собственный пользовательский интерфейс контактов. Я хочу получить массив контактов по разделам. Другими словами, извлечение всех контактов в разделе A, B, C и т. д. Я пытаюсь отобразить их в виде таблицы, но у меня возникают сложности из-за того, что мне нужно учитывать каждый ASCII и не -ASCII-символы, а также разные языки! Поэтому я подумал, что было бы проще просто получить по разделам, а не по всему списку. - person ; 06.01.2018