iOS開發之AddressBook框架詳解
一、寫在前面
首先,AddressBook框架是一個已經過時的框架,iOS9之後官方提供了Contacts框架來進行用戶通訊錄相關操作。儘管如此,AddressBook框架依然是一個非常優雅並且使用方便的通訊錄幫助庫。本篇博客只要總結AddressBook框架的相關使用方法。
在AddressBook框架中,兩個最重要的數據模型爲ABAddressbookRef與ABRecordRef。前者我們可以理解爲通訊錄的抽象對象,用它來具體操作通訊錄的行爲,後者可以理解爲通訊錄中記錄的抽象對象,其中封裝了聯繫人的相關信息。如下圖所示:
二、關於用戶權限申請
在應用程序內,若需要使用用戶的通訊錄權限需要徵得用戶的同意(畢竟通訊錄屬於用戶隱私)。因此,在使用之前,開發者首先需要進行權限的申請,首先,需要在info.plist文件中添加如下鍵:
Privacy - Contacts Usage Description
使用如下代碼進行使用權限的申請:
//獲取用戶授權狀態 ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus(); //如果尚未獲取過授權 進行授權申請 if (status==kABAuthorizationStatusNotDetermined) { //創建通訊錄對象 這個方法中第1個參數爲預留參數 傳NULL 即可 第2個參數可以傳一個CFErrorRef的指針 ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL); //請求授權 ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) { if (granted) {//請求授權頁面用戶同意授權 //可以進行使用 } //釋放內存 CFRelease(addressBookRef); }); }
ABAuthorizationStatus是授權狀態的枚舉,意義如下:
typedef CF_ENUM(CFIndex, ABAuthorizationStatus) { kABAuthorizationStatusNotDetermined = 0, // 尚未申請過授權 kABAuthorizationStatusRestricted, // 授權被限制 無法使用 kABAuthorizationStatusDenied, // 用戶拒絕授權 kABAuthorizationStatusAuthorized // 已經授權 }
三、獲取基礎的通訊錄信息
下面代碼演示瞭如何獲取基礎的通訊錄聯繫人信息:
//獲取通訊錄 ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL); //獲取聯繫人數量 CFIndex personCount = ABAddressBookGetPersonCount(addressBook); //拿到所有聯繫人 CFArrayRef peopleArray = ABAddressBookCopyArrayOfAllPeople(addressBook); for (int i = 0; i < personCount; i++) { //獲取記錄 ABRecordRef person = CFArrayGetValueAtIndex(peopleArray, i); //拿到姓名 //姓 需要轉換成NSString類型 NSString *lastNameValue = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty); //名 NSString *firstNameValue = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty); NSLog(@"%@:%@",lastNameValue,firstNameValue); //拿到電話 電話可能有多個 ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty); //解析電話數據 CFIndex phoneCount = ABMultiValueGetCount(phones); for (int j = 0; j < phoneCount ; j++) { //電話標籤本地化(例如是住宅,工作等) NSString *phoneLabel = (__bridge_transfer NSString *)ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(phones, j)); //拿到標籤下對應的電話號碼 NSString *phoneValue = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(phones, j); NSLog(@"%@:%@",phoneLabel,phoneValue); } CFRelease(phones); //郵箱 可能多個 ABMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty); CFIndex emailCount = ABMultiValueGetCount(emails); for (int k = 0; k < emailCount; k++) { NSString *emailLabel = (__bridge_transfer NSString *)ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(emails, k)); NSString *emailValue = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(emails, k); NSLog(@"%@:%@",emailLabel,emailValue); } NSLog(@"=============="); CFRelease(emails); } CFRelease(addressBook); CFRelease(peopleArray);
打印信息如下:
關於可獲取到的聯繫人屬性,鍵值列舉如下:
//名 kABPersonFirstNameProperty //姓 kABPersonLastNameProperty //中間名 kABPersonMiddleNameProperty //前綴 用戶在存儲聯繫人時 可以添加自定義的前綴 例如 女士 男士等等 kABPersonPrefixProperty //後綴 kABPersonSuffixProperty //暱稱 kABPersonNicknameProperty //姓發音說明字段 用戶自定義的 kABPersonFirstNamePhoneticProperty //名發音說明字段 用戶自定義的 kABPersonLastNamePhoneticProperty //中間名發音說明字段 用戶自定義的 kABPersonMiddleNamePhoneticProperty //公司名 kABPersonOrganizationProperty //部門名 kABPersonDepartmentProperty //頭銜 kABPersonJobTitleProperty //電子郵件信息 返回一組 需要手動解析 kABPersonEmailProperty //返回生日信息 日期對象 kABPersonBirthdayProperty //筆記信息 kABPersonNoteProperty //記錄的創建日期 kABPersonCreationDateProperty //記錄的最後修改日期 kABPersonModificationDateProperty //地址信息 返回 一組 /* 例如: ABMultiValueRef address = ABRecordCopyValue(person, kABPersonAddressProperty); for (int j=0; j<ABMultiValueGetCount(address); j++) { //地址類型 NSString *type = (__bridge NSString *)(ABMultiValueCopyLabelAtIndex(address, j)); NSDictionary * temDic = (__bridge NSDictionary *)(ABMultiValueCopyValueAtIndex(address, j)); //地址字符串,可以按需求格式化 NSString * adress = [NSString stringWithFormat:@"國家:%@\n省:%@\n市:%@\n街道:%@\n郵編:%@",[temDic valueForKey:(NSString*)kABPersonAddressCountryKey],[temDic valueForKey:(NSString*)kABPersonAddressStateKey],[temDic valueForKey:(NSString*)kABPersonAddressCityKey],[temDic valueForKey:(NSString*)kABPersonAddressStreetKey],[temDic valueForKey:(NSString*)kABPersonAddressZIPKey]]; } */ kABPersonAddressProperty //地址字典中的街道信息鍵 kABPersonAddressStreetKey //地址字典中的城市信息鍵 kABPersonAddressCityKey //地址字典中的地區信息鍵 kABPersonAddressStateKey //地址字典中的壓縮碼信息鍵 kABPersonAddressZIPKey //地址字典中的國家信息鍵 kABPersonAddressCountryKey //地址字典中的國家編碼信息鍵 kABPersonAddressCountryCodeKey //獲取 一組 紀念日日期 kABPersonDateProperty //從具體的日期實體中獲取紀念日 標籤 kABPersonAnniversaryLabel //獲取一組電話號碼 kABPersonPhoneProperty //下面這些對應電話類型 kABPersonPhoneMobileLabel kABPersonPhoneIPhoneLabel kABPersonPhoneMainLabel kABPersonPhoneHomeFAXLabel kABPersonPhoneWorkFAXLabel kABPersonPhoneOtherFAXLabel kABPersonPhonePagerLabel //獲取社交相關信息 kABPersonInstantMessageProperty //下面這些對應社交平臺 kABPersonInstantMessageServiceKey kABPersonInstantMessageServiceYahoo kABPersonInstantMessageServiceJabber kABPersonInstantMessageServiceMSN kABPersonInstantMessageServiceICQ kABPersonInstantMessageServiceAIM kABPersonInstantMessageServiceQQ kABPersonInstantMessageServiceGoogleTalk kABPersonInstantMessageServiceSkype kABPersonInstantMessageServiceFacebook kABPersonInstantMessageServiceGaduGadu //社交用戶名 kABPersonInstantMessageUsernameKey //獲取一組url kABPersonURLProperty //url相關標籤 kABPersonHomePageLabel //關聯信息 kABPersonRelatedNamesProperty //關聯信息相關的標籤 kABPersonFatherLabel kABPersonMotherLabel kABPersonParentLabel kABPersonBrotherLabel kABPersonSisterLabel kABPersonChildLabel kABPersonFriendLabel kABPersonSpouseLabel kABPersonPartnerLabel kABPersonAssistantLabel kABPersonManagerLabel //獲取社交賬戶相關 kABPersonSocialProfileProperty //社交賬戶相關key kABPersonSocialProfileURLKey kABPersonSocialProfileServiceKey kABPersonSocialProfileUsernameKey kABPersonSocialProfileUserIdentifierKey kABPersonSocialProfileServiceTwitter kABPersonSocialProfileServiceSinaWeibo kABPersonSocialProfileServiceGameCenter kABPersonSocialProfileServiceFacebook kABPersonSocialProfileServiceMyspace kABPersonSocialProfileServiceLinkedIn kABPersonSocialProfileServiceFlickr //週期性日期信息 kABPersonAlternateBirthdayProperty //週期性日期相關鍵 kABPersonAlternateBirthdayCalendarIdentifierKey kABPersonAlternateBirthdayEraKey kABPersonAlternateBirthdayYearKey kABPersonAlternateBirthdayMonthKey kABPersonAlternateBirthdayIsLeapMonthKey kABPersonAlternateBirthdayDayKey
四、關於ABMultiValueRef
如上所述,我們在獲取聯繫人相關信息時,很多場景都會獲取一組,例如對於電話號碼,地址,郵箱這類的數據。在AddressBook框架中,這一組數據被封裝爲ABMultiValueRef對象。對於ABMultiValueRef對象,有如下方法可以對其進行處理:
//獲取內部封裝值的類型 /* enum { kABInvalidPropertyType = 0x0, // 無效 kABStringPropertyType = 0x1, // 字符串 kABIntegerPropertyType = 0x2, // 整數 kABRealPropertyType = 0x3, // 屬性 kABDateTimePropertyType = 0x4, // 時間 kABDictionaryPropertyType = 0x5, // 字典 kABMultiStringPropertyType = kABMultiValueMask | kABStringPropertyType, // 聚合字符串 kABMultiIntegerPropertyType = kABMultiValueMask | kABIntegerPropertyType, // 聚合整型 kABMultiRealPropertyType = kABMultiValueMask | kABRealPropertyType, // 聚合屬性 kABMultiDateTimePropertyType = kABMultiValueMask | kABDateTimePropertyType, // 聚合時間 kABMultiDictionaryPropertyType = kABMultiValueMask | kABDictionaryPropertyType, // 聚合字典 }; */ ABPropertyType ABMultiValueGetPropertyType(ABMultiValueRef multiValue) //獲取其中封裝的值的個數 CFIndex ABMultiValueGetCount(ABMultiValueRef multiValue) //根據索引獲取值 ABMultiValueCopyValueAtIndex(ABMultiValueRef multiValue, CFIndex index) //獲取所有的值 組成數組 CFArrayRef ABMultiValueCopyArrayOfAllValues(ABMultiValueRef multiValue) //根據索引獲取標籤 CFStringRef ABMultiValueCopyLabelAtIndex(ABMultiValueRef multiValue, CFIndex index) //根據ID獲取值 ABMultiValueIdentifier ABMultiValueGetIdentifierAtIndex(ABMultiValueRef multiValue, CFIndex index) //根據ID 獲取索引 CFIndex ABMultiValueGetIndexForIdentifier(ABMultiValueRef multiValue, ABMultiValueIdentifier identifier) //獲取第一個值 CFIndex ABMultiValueGetFirstIndexOfValue(ABMultiValueRef multiValue, CFTypeRef value) //下面這些與寫聯繫人信息相關 //創建一個可變的數據對象 ABMutableMultiValueRef ABMultiValueCreateMutable(ABPropertyType type) //爲某個標籤添加值 bool ABMultiValueAddValueAndLabel(ABMutableMultiValueRef multiValue, CFTypeRef value, CFStringRef label, ABMultiValueIdentifier *outIdentifier) //在某個索引處插入 標籤和值 bool ABMultiValueInsertValueAndLabelAtIndex(ABMutableMultiValueRef multiValue, CFTypeRef value, CFStringRef label, CFIndex index, ABMultiValueIdentifier *outIdentifier) //刪除某個索引處的標籤和值 bool ABMultiValueRemoveValueAndLabelAtIndex(ABMutableMultiValueRef multiValue, CFIndex index) //替換某個索引處的值 bool ABMultiValueReplaceValueAtIndex(ABMutableMultiValueRef multiValue, CFTypeRef value, CFIndex index) //替換某個索引處的標籤 bool ABMultiValueReplaceLabelAtIndex(ABMutableMultiValueRef multiValue, CFStringRef label, CFIndex index)
五、ABRecordRef對象
ABRecordRef雖然很多時候,我們可以把它理解爲聯繫人對象,但是其實並不正確,實際上它是一個抽象的記錄對象,在AddressBook框架中有3種類型的ABRecordRef:
enum { kABPersonType = 0, //聯繫人類型 kABGroupType = 1, //組類型 kABSourceType = 2 //資源類型 };
可以操作ABRecordRef的方法非常簡單,無非讀與寫,如下:
//獲取記錄類型 ABRecordType ABRecordGetRecordType(ABRecordRef record); //獲取記錄中的數據 CFTypeRef ABRecordCopyValue(ABRecordRef record, ABPropertyID property); //設置記錄中的數據 bool ABRecordSetValue(ABRecordRef record, ABPropertyID property, CFTypeRef value, CFErrorRef* error); //刪除記錄中的數據 bool ABRecordRemoveValue(ABRecordRef record, ABPropertyID property, CFErrorRef* error);
六、聯繫人組
在iOS系統的聯繫人應用中,我們可以對聯繫人進行分組,如下圖所示:
AddressBook框架中的如下方法與聯繫人組操作相關:
//創建一個聯繫人組記錄 ABRecordRef ABGroupCreate(void); //在指定的資源中創建 ABRecordRef ABGroupCreateInSource(ABRecordRef source); //獲取資源 ABRecordRef ABGroupCopySource(ABRecordRef group); //獲取組中的所有成員 CFArrayRef ABGroupCopyArrayOfAllMembers(ABRecordRef group); //根據指定的排序方法獲取組中所有成員 CFArrayRef ABGroupCopyArrayOfAllMembersWithSortOrdering(ABRecordRef group, ABPersonSortOrdering sortOrdering); //向組中添加成員 bool ABGroupAddMember(ABRecordRef group, ABRecordRef person, CFErrorRef* error); //刪除組中的成員 bool ABGroupRemoveMember(ABRecordRef group, ABRecordRef member, CFErrorRef* error); //根據某條記錄獲取組 ABRecordRef ABAddressBookGetGroupWithRecordID(ABAddressBookRef addressBook, ABRecordID recordID); //獲取通訊錄中所有組的個數 CFIndex ABAddressBookGetGroupCount(ABAddressBookRef addressBook); //獲取通訊錄中所有組 CFArrayRef ABAddressBookCopyArrayOfAllGroups(ABAddressBookRef addressBook); CFArrayRef ABAddressBookCopyArrayOfAllGroupsInSource(ABAddressBookRef addressBook, ABRecordRef source);
七、重中之重——ABAddressBookRef對象
前面介紹了許多操作聯繫人,操作組的方法,所有這些操作的基礎都是基於一個通訊錄對象ABAddressBookRef的。除了最前面演示的申請權限的相關方法,如下列舉了與ABAddressBookRef相關的其他常用函數:
//存儲通訊錄 bool ABAddressBookSave(ABAddressBookRef addressBook, CFErrorRef* error); //獲取通訊錄是否有未保存的更改 bool ABAddressBookHasUnsavedChanges(ABAddressBookRef addressBook); //向通訊錄中添加一條記錄 bool ABAddressBookAddRecord(ABAddressBookRef addressBook, ABRecordRef record, CFErrorRef* error); //移除通訊錄中的一條記錄 bool ABAddressBookRemoveRecord(ABAddressBookRef addressBook, ABRecordRef record, CFErrorRef* error); //註冊通訊錄發生變化時的外部監聽 void ABAddressBookRegisterExternalChangeCallback(ABAddressBookRef addressBook, ABExternalChangeCallback callback, void *context); //移除通訊錄發生變化時的外部監聽 void ABAddressBookUnregisterExternalChangeCallback(ABAddressBookRef addressBook, ABExternalChangeCallback callback, void *context); //將未保存的更改重置 void ABAddressBookRevert(ABAddressBookRef addressBook); //工具方法,進行標籤的本地化轉換 CFStringRef ABAddressBookCopyLocalizedLabel(CFStringRef label);