Skip to content

Instantly share code, notes, and snippets.

@loganwright
Created January 9, 2015 19:03
Show Gist options
  • Save loganwright/8865d4ea792b9533cf7b to your computer and use it in GitHub Desktop.
Save loganwright/8865d4ea792b9533cf7b to your computer and use it in GitHub Desktop.
SwiftContact - Converting ABRecordRef to Clean, usable contact object
//
// Contact.swift
// FriendLender
//
// Created by Logan Wright on 9/22/14.
// Copyright (c) 2014 lowriDevs. All rights reserved.
//
import UIKit
import AddressBook
// MARK: Debugging
private let ContactDebug = false
// MARK: NSCoding Constants
private let ContactFirstNameKey = "firstNameKey"
private let ContactFirstNamePhoneticKey = "firstNamePhoneticKey"
private let ContactMiddleNameKey = "middleNameKey"
private let ContactMiddleNamePhoneticKey = "middleNamePhoneticKey"
private let ContactLastNameKey = "lastNameKey"
private let ContactLastNamePhoneticKey = "lastNamePhoneticKey"
private let ContactPrefixKey = "prefixKey"
private let ContactSuffixKey = "suffixKey"
private let ContactNicknameKey = "nicknameKey"
private let ContactBirthDateKey = "birthDateKey"
private let ContactCreationDateKey = "creationDateKey"
private let ContactModificationDateKey = "modificationDateKey"
private let ContactOrganizationKey = "organizationKey"
private let ContactJobTitleKey = "jobTitleKey"
private let ContactDepartmentKey = "departmentKey"
private let ContactPhoneNumbersKey = "phoneNumbersKey"
private let ContactEmailsKey = "emailsKey"
private let ContactNoteKey = "noteKey"
// MARK: Contact Class
class Contact: NSObject, NSCoding {
// MARK: Names
var firstName: String?
var firstNamePhonetic: String?
var middleName: String?
var middleNamePhonetic: String?
var lastName: String?
var lastNamePhonetic: String?
var prefix: String? // Prefix ("Sir" "Duke" "General") - kABStringPropertyType
var suffix: String? // Suffix ("Jr." "Sr." "III") - kABStringPropertyType
var nickname: String?
var fullName: String? {
get {
var fullName: String = ""
if let firstName = self.firstName {
fullName += firstName
}
if let lastName = self.lastName {
if countElements(fullName) > 0 {
fullName += " "
}
fullName += lastName
}
return countElements(fullName) > 0 ? fullName : nil
}
}
// MARK: Dates
var birthDate: NSDate?
var creationDate: NSDate?
var modificationDate: NSDate?
// MARK: Work
var organization: String?
var jobTitle: String?
var department: String?
// MARK: Contact Information
var phoneNumbers: PhoneNumbers?
var emails: Emails?
// MARK: Additional Info
var note: String?
// MARK: Initialization
init(recordRef: ABRecordRef) {
super.init()
self.initializeNamesWithABRecordRef(recordRef)
self.initializeWorkInfoWithABRecordRef(recordRef)
self.initializeCreationInfoWithABRecordRef(recordRef)
self.birthDate = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonBirthdayProperty) as? NSDate
self.note = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonNoteProperty) as? String
if let phonesRef: Unmanaged<AnyObject> = ABRecordCopyValue(recordRef, kABPersonPhoneProperty){
self.phoneNumbers = PhoneNumbers(phoneNumbersRef: phonesRef.takeRetainedValue() as ABMultiValueRef)
}
if let emailRef: Unmanaged<AnyObject> = ABRecordCopyValue(recordRef, kABPersonEmailProperty){
self.emails = Emails(emailsRef: emailRef.takeRetainedValue() as ABMultiValueRef)
}
}
func initializeNamesWithABRecordRef(recordRef: ABRecordRef) {
self.firstName = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonFirstNameProperty) as? String
self.firstNamePhonetic = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonFirstNamePhoneticProperty) as? String
self.lastName = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonLastNameProperty) as? String
self.lastNamePhonetic = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonLastNamePhoneticProperty) as? String
self.middleName = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonMiddleNameProperty) as? String
self.middleNamePhonetic = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonMiddleNamePhoneticProperty) as? String
self.prefix = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonPrefixProperty) as? String
self.suffix = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonSuffixProperty) as? String
self.nickname = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonNicknameProperty) as? String
}
func initializeWorkInfoWithABRecordRef(recordRef: ABRecordRef) {
self.organization = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonOrganizationProperty) as? String
self.jobTitle = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonJobTitleProperty) as? String
self.department = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonDepartmentProperty) as? String
}
func initializeCreationInfoWithABRecordRef(recordRef: ABRecordRef) {
self.creationDate = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonCreationDateProperty) as? NSDate
self.modificationDate = self.rawValueFromABRecordRef(recordRef, forProperty: kABPersonModificationDateProperty) as? NSDate
}
// MARK: Getting Properties From RecordRef
func rawValueFromABRecordRef(recordRef: ABRecordRef, forProperty property: ABPropertyID) -> AnyObject? {
return ABRecordCopyValue(recordRef, property).takeRetainedValue()
}
// MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder) {
if firstName != nil {aCoder.encodeObject(firstName!, forKey: ContactFirstNameKey)}
if firstNamePhonetic != nil {aCoder.encodeObject(firstNamePhonetic!, forKey: ContactFirstNamePhoneticKey)}
if middleName != nil {aCoder.encodeObject(middleName!, forKey: ContactMiddleNameKey)}
if middleNamePhonetic != nil {aCoder.encodeObject(middleNamePhonetic!, forKey: ContactMiddleNamePhoneticKey)}
if lastName != nil {aCoder.encodeObject(lastName!, forKey: ContactLastNameKey)}
if lastNamePhonetic != nil {aCoder.encodeObject(lastNamePhonetic!, forKey: ContactLastNamePhoneticKey)}
if prefix != nil {aCoder.encodeObject(prefix!, forKey: ContactPrefixKey)}
if suffix != nil {aCoder.encodeObject(suffix!, forKey: ContactSuffixKey)}
if nickname != nil {aCoder.encodeObject(nickname!, forKey: ContactNicknameKey)}
if birthDate != nil {aCoder.encodeObject(birthDate!, forKey: ContactBirthDateKey)}
if creationDate != nil {aCoder.encodeObject(creationDate!, forKey: ContactCreationDateKey)}
if modificationDate != nil {aCoder.encodeObject(modificationDate!, forKey: ContactModificationDateKey)}
if organization != nil {aCoder.encodeObject(organization!, forKey: ContactOrganizationKey)}
if jobTitle != nil {aCoder.encodeObject(jobTitle!, forKey: ContactJobTitleKey)}
if department != nil {aCoder.encodeObject(department!, forKey: ContactDepartmentKey)}
if phoneNumbers != nil {aCoder.encodeObject(phoneNumbers!, forKey: ContactPhoneNumbersKey)}
if emails != nil {aCoder.encodeObject(emails!, forKey: ContactEmailsKey)}
if note != nil {aCoder.encodeObject(note!, forKey: ContactNoteKey)}
}
required init(coder aDecoder: NSCoder) {
self.firstName = aDecoder.decodeObjectForKey(ContactFirstNameKey) as? String
self.firstNamePhonetic = aDecoder.decodeObjectForKey(ContactFirstNamePhoneticKey) as? String
self.middleName = aDecoder.decodeObjectForKey(ContactMiddleNameKey) as? String
self.middleNamePhonetic = aDecoder.decodeObjectForKey(ContactMiddleNamePhoneticKey) as? String
self.lastName = aDecoder.decodeObjectForKey(ContactLastNameKey) as? String
self.lastNamePhonetic = aDecoder.decodeObjectForKey(ContactLastNamePhoneticKey) as? String
self.prefix = aDecoder.decodeObjectForKey(ContactPrefixKey) as? String
self.suffix = aDecoder.decodeObjectForKey(ContactSuffixKey) as? String
self.nickname = aDecoder.decodeObjectForKey(ContactNicknameKey) as? String
self.birthDate = aDecoder.decodeObjectForKey(ContactBirthDateKey) as? NSDate
self.creationDate = aDecoder.decodeObjectForKey(ContactCreationDateKey) as? NSDate
self.modificationDate = aDecoder.decodeObjectForKey(ContactModificationDateKey) as? NSDate
self.organization = aDecoder.decodeObjectForKey(ContactOrganizationKey) as? String
self.jobTitle = aDecoder.decodeObjectForKey(ContactJobTitleKey) as? String
self.department = aDecoder.decodeObjectForKey(ContactDepartmentKey) as? String
self.phoneNumbers = aDecoder.decodeObjectForKey(ContactPhoneNumbersKey) as? PhoneNumbers
self.emails = aDecoder.decodeObjectForKey(ContactEmailsKey) as? Emails
self.note = aDecoder.decodeObjectForKey(ContactNoteKey) as? String
}
}
// MARK: Emails Class NSCoding Constants
private let EmailsHomeKey = "EmailsHomeKey"
private let EmailsWorkKey = "EmailsWorkKey"
private let EmailsOtherKey = "EmailsOtherKey"
private let EmailsUnknownEmailsKey = "EmailsUnknownEmailsKey"
// MARK: Emails
class Emails: NSObject, NSCoding {
// MARK: Public Properties
var home: String?
var work: String?
var other: String?
var unknownEmails: [String : String] = [:]
var bestEmail: String? {
get {
var bestEmail: String?
if let home = home {
bestEmail = home
} else if let work = work {
bestEmail = work
} else if let other = other {
bestEmail = other
} else if unknownEmails.count > 0 {
bestEmail = unknownEmails.values.first
}
return bestEmail
}
}
// MARK: Initialization
init(emailsRef: ABMultiValueRef) {
super.init()
for i in 0..<ABMultiValueGetCount(emailsRef) {
let emailKey = (ABMultiValueCopyLabelAtIndex(emailsRef, i).takeRetainedValue() as CFStringRef)
let emailValue = ABMultiValueCopyValueAtIndex(emailsRef, i).takeRetainedValue() as String
if emailKey == kABHomeLabel {
home = emailValue
}
else if emailKey == kABWorkLabel {
work = emailValue
}
else if emailKey == kABOtherLabel {
other = emailValue
}
else {
if ContactDebug {println("UnknownEmailKey: \(emailKey) Val: \(emailValue)")}
/*
I implemented all of the available keys as well as Home, not sure if other keys exist. Just in case, I store unexpected values here.
*/
unknownEmails[emailKey] = emailValue
}
if ContactDebug {
println("EmailKey: \(emailKey)")
println("EmailVal: \(emailValue)")
}
}
}
// MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder) {
if home != nil {aCoder.encodeObject(home!, forKey: EmailsHomeKey)}
if work != nil {aCoder.encodeObject(work!, forKey: EmailsWorkKey)}
if other != nil {aCoder.encodeObject(other!, forKey: EmailsOtherKey)}
if unknownEmails.count > 0 {aCoder.encodeObject(unknownEmails, forKey: EmailsUnknownEmailsKey)}
}
required init(coder aDecoder: NSCoder) {
home = aDecoder.decodeObjectForKey(EmailsHomeKey) as? String
work = aDecoder.decodeObjectForKey(EmailsWorkKey) as? String
other = aDecoder.decodeObjectForKey(EmailsOtherKey) as? String
if let decodedUnknownEmails = aDecoder.decodeObjectForKey(EmailsUnknownEmailsKey) as? [String : String] {
unknownEmails = decodedUnknownEmails
}
}
}
// MARK: Phone Numbers Class NSCoding Constants
private let PhoneNumbersMobileKey = "PhoneNumbersMobileKey"
private let PhoneNumbersIPhoneKey = "PhoneNumbersIPhoneKey"
private let PhoneNumbersMainKey = "PhoneNumbersMainKey"
private let PhoneNumbersFaxHomeKey = "PhoneNumbersFaxHomeKey"
private let PhoneNumbersFaxWorkKey = "PhoneNumbersFaxWorkKey"
private let PhoneNumbersFaxOtherKey = "PhoneNumbersFaxOtherKey"
private let PhoneNumbersPagerKey = "PhoneNumbersPagerKey"
private let PhoneNumbersHomeKey = "PhoneNumbersHomeKey"
private let PhoneNumbersUnknownNumbersKey = "PhoneNumbersUnknownNumbersKey"
// MARK: PhoneNumbers
class PhoneNumbers: NSObject, NSCoding {
// MARK: Public Properties
var mobile: String?
var iPhone: String?
var main: String?
var home: String?
var faxHome: String?
var faxWork: String?
var faxOther: String?
var pager: String?
var unknownNumbers: [String : String] = [:]
var bestTextNumber: String? {
get {
var textNumber: String?
if let iPhone = iPhone {
textNumber = iPhone
} else if let mobile = mobile {
textNumber = mobile
} else if let main = main {
textNumber = main
}
return textNumber
}
}
var bestPhoneNumber: String? {
get {
var phoneNumber: String?
if let iPhone = iPhone {
phoneNumber = iPhone
} else if let mobile = mobile {
phoneNumber = mobile
} else if let main = main {
phoneNumber = main
} else if let home = home {
phoneNumber = home
}
return phoneNumber
}
}
// MARK: Initialization
init(phoneNumbersRef: ABMultiValueRef) {
super.init()
for i in 0..<ABMultiValueGetCount(phoneNumbersRef) {
let phoneNumberKey = (ABMultiValueCopyLabelAtIndex(phoneNumbersRef, i).takeRetainedValue() as CFStringRef)
let phoneNumberValue = ABMultiValueCopyValueAtIndex(phoneNumbersRef, i).takeRetainedValue() as String
if phoneNumberKey == kABPersonPhoneMobileLabel {
self.mobile = phoneNumberValue;
}
else if phoneNumberKey == kABPersonPhoneIPhoneLabel {
self.iPhone = phoneNumberValue;
}
else if phoneNumberKey == kABPersonPhoneMainLabel {
self.main = phoneNumberValue;
}
else if phoneNumberKey == kABPersonPhoneHomeFAXLabel {
self.faxHome = phoneNumberValue;
}
else if phoneNumberKey == kABPersonPhoneWorkFAXLabel {
self.faxWork = phoneNumberValue;
}
else if phoneNumberKey == kABPersonPhoneOtherFAXLabel {
self.faxOther = phoneNumberValue;
}
else if phoneNumberKey == kABPersonPhonePagerLabel {
self.pager = phoneNumberValue
}
else if phoneNumberKey == "_$!<Home>!$_" {
self.home = phoneNumberValue
}
else {
if ContactDebug {println("UnknownPhoneNumberKey: \(phoneNumberKey) Val: \(phoneNumberValue)")}
/*
I implemented all of the available keys as well as Home, not sure if other keys exist. Just in case, I store unexpected values here.
*/
unknownNumbers[phoneNumberKey] = phoneNumberValue
}
if ContactDebug {
println("PhoneNumberKey: \(phoneNumberKey)")
println("PhoneNumberVal: \(phoneNumberValue)")
}
}
}
// MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder) {
if mobile != nil {aCoder.encodeObject(mobile!, forKey: PhoneNumbersMobileKey)}
if iPhone != nil {aCoder.encodeObject(iPhone!, forKey: PhoneNumbersIPhoneKey)}
if main != nil {aCoder.encodeObject(main!, forKey: PhoneNumbersMainKey)}
if faxHome != nil {aCoder.encodeObject(faxHome!, forKey: PhoneNumbersFaxHomeKey)}
if faxWork != nil {aCoder.encodeObject(faxWork!, forKey: PhoneNumbersFaxWorkKey)}
if faxOther != nil {aCoder.encodeObject(faxOther!, forKey: PhoneNumbersFaxOtherKey)}
if pager != nil {aCoder.encodeObject(pager!, forKey: PhoneNumbersPagerKey)}
if home != nil {aCoder.encodeObject(home!, forKey: PhoneNumbersHomeKey)}
if unknownNumbers.count > 0 {aCoder.encodeObject(unknownNumbers, forKey: PhoneNumbersUnknownNumbersKey)}
}
required init(coder aDecoder: NSCoder) {
mobile = aDecoder.decodeObjectForKey(PhoneNumbersMobileKey) as? String
iPhone = aDecoder.decodeObjectForKey(PhoneNumbersIPhoneKey) as? String
main = aDecoder.decodeObjectForKey(PhoneNumbersMainKey) as? String
faxHome = aDecoder.decodeObjectForKey(PhoneNumbersFaxHomeKey) as? String
faxWork = aDecoder.decodeObjectForKey(PhoneNumbersFaxWorkKey) as? String
faxOther = aDecoder.decodeObjectForKey(PhoneNumbersFaxOtherKey) as? String
pager = aDecoder.decodeObjectForKey(PhoneNumbersPagerKey) as? String
home = aDecoder.decodeObjectForKey(PhoneNumbersHomeKey) as? String
if let decodedUnknownNumbersDict = aDecoder.decodeObjectForKey(PhoneNumbersUnknownNumbersKey) as? [String : String] {
unknownNumbers = decodedUnknownNumbersDict
}
}
}
@arytbk
Copy link

arytbk commented Mar 24, 2015

This crashes with Xcode 6.3 beta 3 for nil values. I fixed it this way:

  func rawValueFromABRecordRef(recordRef: ABRecordRef, forProperty property: ABPropertyID) -> AnyObject? {
    if let val = ABRecordCopyValue(recordRef, property) {
        return val.takeRetainedValue()
    } else {
        return nil
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment