1. 程式人生 > >iOS開發之AddressBook框架詳解

iOS開發之AddressBook框架詳解

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