Optimal filtering of nested filters?

I tried to do something in Swift, which would be easy in Objective-C using KVC. The new Contact Structure added in iOS9 is for the most part easier to use than the old address book API . But finding a contact by mobile phone number seems difficult. The predicates provided for finding contacts are limited by name and unique identifier. In Objective-C, you can get all the contacts and then use NSPredicate to filter on KVC request. Structure:

CNContact-> phoneNumbers → (String, CNPhoneNumber-> stringValue)

In the code below, suppose I selected contacts through:

let keys = [CNContactEmailAddressesKey,CNContactPhoneNumbersKey, CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName)] let fetchRequest = CNContactFetchRequest(keysToFetch: keys) var contacts:[CNContact] = [] try! CNContactStore().enumerateContactsWithFetchRequest(fetchRequest) { ... 

I want to compare stringValue with a known value. Here is what I still have from the playground:

 import UIKit import Contacts let JennysPhone = "111-867-5309" let SomeOtherPhone = "111-111-2222" let AndAThirdPhone = "111-222-5309" let contact1 = CNMutableContact() contact1.givenName = "Jenny" let phone1 = CNPhoneNumber(stringValue: JennysPhone) let phoneLabeled1 = CNLabeledValue(label: CNLabelPhoneNumberMobile, value: phone1) contact1.phoneNumbers.append(phoneLabeled1) let contact2 = CNMutableContact() contact2.givenName = "Billy" let phone2 = CNPhoneNumber(stringValue: SomeOtherPhone) let phoneLabeled2 = CNLabeledValue(label: CNLabelPhoneNumberMobile, value: phone2) contact2.phoneNumbers.append(phoneLabeled2) let contact3 = CNMutableContact() contact3.givenName = "Jimmy" let phone3 = CNPhoneNumber(stringValue: SomeOtherPhone) let phoneLabeled3 = CNLabeledValue(label: CNLabelPhoneNumberMobile, value: phone3) contact3.phoneNumbers.append(phoneLabeled3) let contacts = [contact1, contact2, contact3] let matches = contacts.filter { (contact) -> Bool in let phoneMatches = contact.phoneNumbers.filter({ (labeledValue) -> Bool in if let v = labeledValue.value as? CNPhoneNumber { return v.stringValue == JennysPhone } return false }) return phoneMatches.count > 0 } if let jennysNum = matches.first?.givenName { print("I think I found Jenny: \(jennysNum)") } else { print("I could not find Jenny") } 

It works, but is inefficient. On the device, I will need to run this in the background thread, and this may take some time if a person has many contacts. Is there a better way to find a contact by phone number (or email address, same idea) using the new iOS contact infrastructure?

+4
source share
2 answers

If you are looking for a faster way:

 let matches = contacts.filter { return $0.phoneNumbers .flatMap { $0.value as? CNPhoneNumber } .contains { $0.stringValue == JennysPhone } } 

.flatMap outputs each member of the phoneNumbers from the CNLabeledValue type to the CNPhoneNumber type, ignoring those that cannot be executed.

.contains checks if any of these numbers matches Jenny's number.

+5
source

I assume you want a faster way, but obviously everything you can do in Obj-C can also be done in fast mode. So you can still use NSPredicate:

 let predicate = NSPredicate(format: "ANY phoneNumbers.value.digits CONTAINS %@", "1118675309") let contactNSArray = contacts as NSArray let contactsWithJennysPhoneNumber = contactNSArray.filteredArrayUsingPredicate(predicate) 
+3
source

All Articles