Swift: Copy Information Selected by User in ABPersonViewController to Dictionary

我的未来我决定 提交于 2019-12-09 13:46:30

问题


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).


回答1:


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.




回答2:


I'm afraid there can't be a generic method to retrieve information from AddressBook because there are too many different collection and property types.

This is an example to get first name, last name and address information when the user taps on an address field.

It's assumed that the dictionary s is declared as

var s = Dictionary<String, AnyObject>()

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

    addPropertyForKey("FirstName", person : person, property: kABPersonFirstNameProperty)
    addPropertyForKey("LastName", person : person, property: kABPersonLastNameProperty)

    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"])

      println(s)
    }

    return false
  }

  func addPropertyForKey(key : String, person: ABRecord, property : ABPropertyID)
  {
    let value : AnyObject = ABRecordCopyValue(person, property).takeUnretainedValue()
    s[key] = value
  }

  func addValuesFromRecord(record : AnyObject,  forKeys keys : [String])
  {
    for key in keys {
      if let value : AnyObject = record[key] {
        s[key] = value
      }
    }
  }

to be as generic as possible, all values are declared as AnyObject



来源:https://stackoverflow.com/questions/31822329/swift-copy-information-selected-by-user-in-abpersonviewcontroller-to-dictionary

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!