swiftdictionaryabaddressbookabpersonviewcontrollerabrecord

Swift: Copy Information Selected by User in ABPersonViewController to Dictionary


I'm trying to implement the func personViewController(personViewController: ABPersonViewController!, shouldPerformDefaultActionForPerson person: ABRecord!, property property: ABPropertyID, identifier valueIdentifier: ABMultiValueIdentifier) -> Bool function, which is part of the ABPersonViewControllerDelegate protocol and is called whenever the user clicks on an item in the ABPersonViewController, such that any information the user selects will be copied to a [String : String] dictionary such that the property name will be the key for the property value: [..."kABPersonFirstNameProperty" : "Alexander"...], say.

I also want to avoid a switch or a long list of conditions testing for if a property is this or that; I'd rather handle it as generally as possible—I'm trying for only two different cases: if the property is a single value or a multi value. If it's a multi-value, I'd like to copy down all the information available.

For example, if the user clicks on the address, the result might be such: [..."kABPersonAddressStreetKey" : "1 Infinite Loop", "kABPersonAddressCityKey" : "Cupertino" , "kABPersonAddressStateKey", "California (or CA?)"...].

This is all I have, after various hours of scouring the Apple Developer Library and related SO questions (pathetic, I know):

func personViewController(personViewController: ABPersonViewController!,
    shouldPerformDefaultActionForPerson person: ABRecord!,
    property property: ABPropertyID,
    identifier valueIdentifier: ABMultiValueIdentifier) -> Bool {

    s["kABPersonFirstNameProperty"] = ABRecordCopyValue(person, kABPersonFirstNameProperty) as! String //the name can't actually be selected by the user, but I want to capture it anyway
    s["kABPersonLastNameProperty"] = ABRecordCopyValue(person, kABPersonLastNameProperty) as! String

    if valueIdentifier == 0 { //property is a single property, not a multivalue property

        let record = ABRecordCopyValue(person, property)
        s[property as! String!] = record as! String

    } else { //property is an ABMultiValue

        let multiRecord = ABRecordCopyValue(person, property) as! ABMultiValueRef
        s[property as! String] = ABMultiValueGetIndexForIdentifier(multiRecord, valueIdentifier)

    }

    return false

}

I can tell there are plenty of pieces missing—is it even possible to condense this into a dictionary?

Thanks in advance (and 100 reputation to whomever provides a complete, correct answer).


Solution

  • Using vadian's answer as a starting point, I compiled the following, which will gather and copy to a dictionary almost all of the information a user can select (and some information which a user can't click on—first name, last name, organization—automatically).

    It's a work in progress, which I'll update as I continue to add support for more properties, but at present it works for first name, last name, email, phone numbers, address, and some social profiles.

    I had to use indirect methods in some cases: storing phone numbers whose labels didn't have a designated key as kAB_Labels, for example, and it doesn't yet take the label of an email address.

    With s as of type Dictionary<String, AnyObject>, here it is:

    func personViewController(personViewController: ABPersonViewController!, shouldPerformDefaultActionForPerson person: ABRecord!, property: ABPropertyID, identifier: ABMultiValueIdentifier) -> Bool {
    
        addPropertyForKey("kABPersonFirstNameProperty", person : person, property: kABPersonFirstNameProperty)
        addPropertyForKey("kABPersonLastNameProperty", person : person, property: kABPersonLastNameProperty)
        addPropertyForKey("kABPersonOrganizationProperty", person: person, property: kABPersonOrganizationProperty)
    
        if (property == kABPersonAddressProperty) {
            let multiRecord : ABMultiValueRef = ABRecordCopyValue(person, property).takeUnretainedValue()
            let index = ABMultiValueGetIndexForIdentifier(multiRecord, identifier)
            let addressRecord :AnyObject = ABMultiValueCopyValueAtIndex(multiRecord, index).takeUnretainedValue()
            addValuesFromRecord(addressRecord, forKeys:["Street", "City", "State", "ZIP", "Country", "CountryCode"])
    
        }
    
        if (property == kABPersonEmailProperty) {
    
            let multiRecord: ABMultiValueRef = ABRecordCopyValue(person, property).takeUnretainedValue()
            let index = ABMultiValueGetIndexForIdentifier(multiRecord, identifier)
            let email = ABMultiValueCopyValueAtIndex(multiRecord, index).takeUnretainedValue() as! String
    
            if (s["kABPersonEmailProperty(1)"] == nil) {
    
                s["kABPersonEmailProperty(1)"] = email
    
            } else {
    
                s["kABPersonEmailProperty(2)"] = email
    
            }
    
        }
    
        if (property == kABPersonSocialProfileProperty) {
    
            let multiRecord: ABMultiValueRef = ABRecordCopyValue(person, property).takeUnretainedValue()
            let index = ABMultiValueGetIndexForIdentifier(multiRecord, identifier)
            let profile = ABMultiValueCopyValueAtIndex(multiRecord, index).takeUnretainedValue()
            let profileType = profile["service"] as! String
            let profileName = profile["username"] as! String
    
            switch profileType {
                case "facebook" : s["kABPersonSocialProfileServiceFacebook"] = profileName
                case  "twitter" : s["kABPersonSocialProfileServiceTwitter"] = profileName
                case "sinaweibo": s["kABPersonSocialProfileServiceSinaWeibo"] = profileName
                default: break
            }
    
    
    
        }
    
        if (property == kABPersonPhoneProperty) {
    
            let multiRecord : ABMultiValueRef = ABRecordCopyValue(person, property).takeUnretainedValue()
            let index = ABMultiValueGetIndexForIdentifier(multiRecord, identifier)
            let number = ABMultiValueCopyValueAtIndex(multiRecord, index).takeUnretainedValue() as! String
    
            let locLabel: CFStringRef = (ABMultiValueCopyLabelAtIndex(multiRecord, index) != nil) ? ABMultiValueCopyLabelAtIndex(multiRecord, index).takeUnretainedValue() as CFStringRef : ""
            let customLabel = String (stringInterpolationSegment: ABAddressBookCopyLocalizedLabel(locLabel))
    
            var cfStr:CFTypeRef = locLabel
            var nsTypeString = cfStr as! NSString
            var a:String = nsTypeString as String
    
            var b = a
    
            if (a.rangeOfString("_$!<") != nil) {
    
                b = a.substringFromIndex(a.rangeOfString("_$!<")!.endIndex)
    
                b = b.substringToIndex(b.rangeOfString(">!$_")!.startIndex)
    
            }
    
            switch b {
    
            case "Mobile" : s["kABPersonPhoneMobileLabel"] = number
            case "iPhone" : s["kABPersonPhoneIPhoneLabel"] = number
            case "Main" : s["kABPersonPhoneMainLabel"] = number
            case "Home" : s["kABHomeLabel"] = number
            case "Work" : s["kABWorkLabel"] = number
            case "Other" : s["kABOtherLabel"] = number
            default: break
    
            }
    
        }
    
        println(s)
    
        return false
    }
    

    Please, anyone should feel free to copy any/all parts of it which would be helpful.