Reputation: 2909
I'm working on an App that works with an ANCS external device. To make things short, the device detects when a SMS comes in and will tell it to the App. Then the App will be able to send a message back to the SMS-sender via a webservice (Twilio).
When the person who sent the SMS is in the Contacts, then the ANCS device sees only the name of this contact, not the phone number itself. So it provides only this readable name to the App. If there is only one name for a contact (for example: "John"), searching for the contact and its phone numbers is quite straightforward, like so:
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.")
}
But sometimes people have several Contacts stored for the same person with different names, for example when a SMS comes in, it could say that it comes from: "John or Dad" - because it corresponds to those 2 Contacts in the address book... (we all have to deal with such duplicates in our address books). And sometimes in the worst cases there can be 3 or more names!
That is where I want to come: how can we distinguish / make a predicate to check for those contacts. The easy-dirty solution would be to split the name on the "or" word, but what if this is a corean, german, sweedish, etc. word? And what if this word belongs to a single Contact whose name is, let's say: "John desk on the roof"? At worst would iOS provide the localized separator word "or"?
Did anyone face such an issue?
Upvotes: 0
Views: 1073
Reputation: 2909
I finally managed to do like this:
1st, I got the list of every transleted or word given by Apple at this address, using for each language the word found in the file TelephonyUtilities.lg
2nd, I created a plist file containing all those words for each preferred language key (en, es, etc)
3rd, when I have a contact name, I grab the correct word string givent the phone preferred language, and I try to split it... If more than 1 split result is found, it means that this is most probably two contacts names for the same contact, so I fetch those contacts to find the common phone number. And it works quite nicely so far.
A few sample code to illustrate (swift 3):
What the plist file looks like (complete one, for whom it might help):
<?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>
Get the or word 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
}
And at last get the possible contacts:
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
}
Upvotes: 0