Search code examples
swiftabaddressbook

Sort contacts with CFArraySortValues in Swift


In Objective-C, I'd sort an array of people as follows:

CFArraySortValues(mutablePeople,
                  CFRangeMake(0, CFArrayGetCount(mutablePeople)),
                  (CFComparatorFunction) ABPersonComparePeopleByName,
                  kABPersonSortByFirstName)

I'm struggling to figure out how to do the same in Swift. The following:

let ctx = UnsafeMutablePointer<Void>.alloc(kABPersonSortByFirstName)

CFArraySortValues(mutablePeople, 
                  CFRangeMake(0, CFArrayGetCount(mutablePeople)), 
                  ABPersonComparePeopleByName, 
                  ctx)

Gives compilation error:

Cannot invoke 'CFArraySortValues' with an argument list of type '(CFMutableArray!, CFRange, (ABRecord!, ABRecord!, ABPersonSortOrdering) -> CFComparisonResult, UnsafeMutablePointer)'

The problem looks to be with the ABPersonComparePeopleByName which would be cast to a CFComparatorFunction in Objective-C.


Solution

  • Here are two alternatives that avoid CFArraySortValues altogether:

    1. Use ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering to retrieve the contacts already sorted:

      let people = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, nil, ABPersonSortOrdering(kABPersonSortByFirstName)).takeRetainedValue() as [ABRecordRef]
      

      If you use nil for the source, it uses all sources (though that behavior is not documented).

    2. Alternatively, if you have a Swift array of ABRecordRef, you can then use the standard Swift sort routine, using ABPersonComparePeopleByName for the comparison:

      var people = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue() as [ABRecordRef]
      people.sort() { ABPersonComparePeopleByName($0, $1, ABPersonSortOrdering(kABPersonSortByFirstName)) != .CompareGreaterThan }
      

    Both of these approaches return a Swift Array<ABRecordRef>, which is a nice native Swift collection. For example, to print the list of the names:

    for record in people {
        let name = ABRecordCopyCompositeName(record).takeRetainedValue()
        println(name)
    }