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