第一大块儿:读取通讯录


1、iOS 6以上系统,争取获取用户允许:

初始化的时候须要推断。设备是否授权
-(id)init{
self = [super init];
[self createdABHandle];
bool isAuthorized = [self isAuthorizedAddressBook];
if (!isAuthorized) {
ABAddressBookRequestAccessWithCompletion(_addressBook, ^(bool granted, CFErrorRef error){
if (granted){
//[self createAddressBuddyData];
}
});
}
return self;
}

看看isAuthorizedAddressBook的内容:

- (BOOL)isAuthorizedAddressBook {
if (SYSTEM_VERSION <= 6.0){
return YES;
}
//
ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
if (status == kABAuthorizationStatusNotDetermined) {
return NO;
}
else if(status == kABAuthorizationStatusAuthorized) {
return YES;
}
else if (status == kABAuthorizationStatusDenied) {
return NO;
}
return NO;
}

2、获取联系人。获取联系人分组

ABAddressBookRef addressBook =ABAddressBookCreate();
NSArray* allPeople = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople (addressBook));
NSArray* allGroups = CFBridgingRelease(ABAddressBookCopyArrayOfAllGroups(addressBook));
for (id person in (NSArray *) allPeople)
[self logContact:person];
for (id group in (NSArray *) allGroups)
[self logGroup:group];
CFRelease(addressBook);

3、联系人字段获取技巧:

● 获取个人或群体完整名称。

比如:NSString* name = (NSString*)ABRecordCopyCompositeName(record);

● 获取联系人ID ABRecordID recId = ABRecordGetRecordID(record);

● 获取电话。邮箱列表,生日等,多键值的方法 比如:ABMultiValueRef phoneNumbersArr = ABRecordCopyValue(record, kABPersonPhoneProperty);

● 获取联系人分组名称 CFStringRef name = ABRecordCopyValue(group,kABGroupNameProperty);

● 获取联系人分组ID   ABRecordID recId = ABRecordGetRecordID(group);

在获取多值的属性时候须要注意:获取地址的时候,多键值有嵌套。

代码例如以下:

NSArray *allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);

int i;
for (i = 0; i < [allPeople count]; i++) {
ABRecordRef record = [allPeople objectAtIndex:i]; ABMutableMultiValueRef multiValue = ABRecordCopyValue(record, kABPersonAddressProperty);
for(CFIndex i=0;i<ABMultiValueGetCount(multiValue);i++)
{
NSString* HomeLabel=(NSString*)ABMultiValueCopyLabelAtInde x(multiValue, i);
if([HomeLabel isEqualTo:@"_$!<Home>!$_"])
{
CFDictionaryRef dict = ABMultiValueCopyValueAtIndex(multiValueArr, i);
NSString* street =CFBridgingRelease( CFDictionaryGetValue(dict, kABPersonAddressStreetKey));
NSString* city =CFBridgingRelease( CFDictionaryGetValue(dict, kABPersonAddressCityKey));
NSString* country =CFBridgingRelease(CFDictionaryGetValue(dict, kABPersonAddressCountryKey));
CFRelease(dict); NSString *syntheticAddress = [NSString stringWithFormat:@"%@, %@, %@"
,(street?street:@"")
,(city?city:@"")
,(country?country:@"")];
}

不能直接调用ABMultiValueCopyValueAtIndex,然后强转成NSString*

4、删除联系人或者群组

ABAddressBookRemoveRecord(addressBook, people, NULL);

ABAddressBookSave(addressBook, NULL);

第二大块儿:写入通讯录

注意。在写入通讯录的时候,有些contact的phone,email,address是多个的。鉴于多线程的安全因素,在使用的时候都是获取拷贝。

/*
更新联系人,比方phoneTypePairArr为空,那么程序把一个空的ABMultiValueRef写入addressBook,达到删除的效果。
*/
-(BOOL) updateToAB:(ABRecordRef)person withContact:(RCSContact*)contact{
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil);
// 保存到联系人对象中,假设属性为nil,则表示要删除
//lastname
NSString* lastName = contact.lastName.length? contact.lastName:nil;
ABRecordSetValue(person, kABPersonLastNameProperty, (__bridge CFStringRef)lastName, NULL); //firstName
NSString* firstName = [contact.firstName length]? contact.firstName:nil;
ABRecordSetValue(person, kABPersonFirstNameProperty, (__bridge CFStringRef)firstName, NULL); //birthday
if ([contact.birthday length]) {
NSDateFormatter *inputFormatter = [[NSDateFormatter alloc] init];
[inputFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]];
[inputFormatter setDateFormat:@"yyyy-MM-dd"];
NSDate* inputDate = [inputFormatter dateFromString:contact.birthday];
ABRecordSetValue(person, kABPersonBirthdayProperty, (__bridge CFDateRef)inputDate, NULL);
}else{
ABRecordSetValue(person, kABPersonBirthdayProperty, NULL, NULL);
} //company name
NSString* companyName = [contact.companyName length]?contact.companyName:nil;
ABRecordSetValue(person, kABPersonOrganizationProperty, (__bridge CFStringRef)companyName, NULL); //company duty
NSString* companyDuty = [contact.companyDuty length]?contact.companyDuty:nil;
ABRecordSetValue(person, kABPersonJobTitleProperty, (__bridge CFStringRef)companyDuty, NULL);
// ABMultiValueRef相似是Objective-C中的NSMutableDictionary
ABMultiValueRef mv = ABMultiValueCreateMutable(kABMultiStringPropertyType);
NSArray* tmpArr = [NSArray arrayWithArray:contact.phoneTypePairArr];
for (PhoneTypePair* p in tmpArr) {
if ([p.content length]==0) {
continue;
}
NSString* label = [_typeDic_ForWrite objectForKey:p.type];
ABMultiValueIdentifier mi = ABMultiValueAddValueAndLabel(mv, (__bridge CFStringRef)p.content, (__bridge CFStringRef)label, &mi);
}
ABRecordSetValue(person, kABPersonPhoneProperty, mv, NULL);
if (mv) { CFRelease(mv);}
//设置邮箱
ABMutableMultiValueRef emailCFArray = ABMultiValueCreateMutable(kABStringPropertyType);
tmpArr = [NSArray arrayWithArray:contact.eMailArr];
for (PhoneTypePair* p in tmpArr) {
//邮件的话
if ([p.content length]==0) {
continue;
}
NSString* label = [_typeDic_ForWrite objectForKey:p.type];
ABMultiValueAddValueAndLabel(emailCFArray,(__bridge CFStringRef)(p.content),(__bridge CFStringRef)label,NULL);
}
ABRecordSetValue(person, kABPersonEmailProperty, emailCFArray, NULL);
if(emailCFArray){CFRelease(emailCFArray);} //设置地址
ABMutableMultiValueRef multiAddress = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);
tmpArr = [NSArray arrayWithArray:contact.addressArr];
for (PhoneTypePair* p in tmpArr) {
//地址的话
if ([p.content length]==0) {
continue;
}
NSMutableDictionary *addressDictionary = [[NSMutableDictionary alloc] init];
[addressDictionary setObject:[p.content mutableCopy] forKey:(NSString *)kABPersonAddressStreetKey];
[addressDictionary setObject:@"" forKey:(NSString *)kABPersonAddressCityKey];
[addressDictionary setObject:@"" forKey:(NSString *)kABPersonAddressStateKey];
[addressDictionary setObject:@"" forKey:(NSString *)kABPersonAddressZIPKey];
[addressDictionary setObject:@"" forKey:(NSString *)kABPersonAddressCountryCodeKey];
//set label
NSString* label = [_typeDic_ForWrite objectForKey:p.type];
ABMultiValueAddValueAndLabel(multiAddress, CFBridgingRetain(addressDictionary), (__bridge CFStringRef)label, NULL);
}
ABRecordSetValue(person, kABPersonAddressProperty, multiAddress, NULL);
if (multiAddress) {CFRelease(multiAddress);} // 设置头像属性
ABPersonRemoveImageData(person, NULL);
ABAddressBookAddRecord(addressBook, person, nil); //设置头像
NSData *data = UIImagePNGRepresentation(contact.portrait);
ABAddressBookSave(addressBook, NULL);
if ([data length]==0) {
ABPersonRemoveImageData(person, NULL);
}else{
CFDataRef cfData = CFDataCreate(NULL, [data bytes], [data length]);
ABPersonSetImageData(person, cfData, nil);
} // 将新建的联系人加入到通讯录中,保存通讯录
ABAddressBookAddRecord(addressBook, person, NULL);
bool isSucess = ABAddressBookSave(addressBook, NULL);
NSLog (@"加入一个人到数据库成功[%d]",isSucess);
// 释放通讯录对象的引用
if (addressBook) {
CFRelease(addressBook);
}
return isSucess;

比如:第35,47,61行,都是採用拷贝数组。防止多线程下读、写数组。导致enumed error

第三大块儿:监听通讯录变更

client代码须要这么实现:

/*
移除注冊函数
*/
-(void)dealloc{
ABAddressBookUnregisterExternalChangeCallback(_addressBook, ContactsChangeCallback, nil);
}
/*
注冊回调函数
*/
- (id)init {
self = [super init];
[self addressBookHandle];
ABAddressBookRegisterExternalChangeCallback(_addressBook, ContactsChangeCallback, nil); return self;
}
/*
回调函数,实现自己的逻辑。 */
void ContactsChangeCallback (ABAddressBookRef addressBook,
CFDictionaryRef info,
void *context){ NSLog(@"ContactsChangeCallback");
}

_addressBook是通讯录句柄。尽管有监听的接口,可是參数info总是空的。

另外:当本APP编辑系统通讯录时候。不会收到通知;通知可能有多个,这时候能够採取:“仅仅处理第一个通知,淹没后面的通知。

”。也能够採取信号量机制,对变更通知一个个在线程中处理。防止界面卡顿。

另外:在加入新的联系人的时候,使用ABNewPersonViewController。特别注意,一定要处理好它的代理:

    // Called when the user selects Save or Cancel. If the new person was saved, person will be
// a valid person that was saved into the Address Book. Otherwise, person will be NULL.
// It is up to the delegate to dismiss the view controller.
- (void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(ABRecordRef)person;

处理不好,造成APP崩溃。

我的处理是这种:

    [self.navigationController popToViewController:self animated:YES];

监听规则:

当App活跃(前台+后台保活期间)的时候,当通讯录改动的时候,会收到通知

当App不活跃的时候(挂起的时候)。App收不到通知;而是,当App到前台的时候收到延迟的通知。

生成vcard

/*************************************************
* @brief 获取通讯录联系列表的vcard
* @param 无
* @retun 无
*************************************************/
- (void)getLocalAddressBookvCard { CFArrayRef persons = ABAddressBookCopyArrayOfAllPeople(_addressBook);
CFDataRef vcards = (CFDataRef)ABPersonCreateVCardRepresentationWithPeople(persons); NSString *vcardString = [[NSString alloc] initWithData:(__bridge NSData *)vcards encoding:NSUTF8StringEncoding]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *folderPath = [paths objectAtIndex:0];
NSString *filePath = [folderPath stringByAppendingPathComponent:@"contacts.vcf"];
NSLog(@"path = %@",filePath); [vcardString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}

生成到document文件夹下的contacts.vcf文件

还能够用以下的办法。更适合生成单个crad

+(NSData*)exportContactsToVcard:(NSArray*)contacts
{
NSMutableArray *people = [NSMutableArray arrayWithCapacity:contacts.count];
ABAddressBookRef ab = ABAddressBookCreate();
for (Contact *contact in contacts)
{
ABRecordRef person = ABAddressBookGetPersonWithRecordID(ab,contact.contactId);
[people addObject:(__bridge id)person];
}
NSData *vCard = (__bridge NSData*)ABPersonCreateVCardRepresentationWithPeople((__bridge CFArrayRef) people);
return vCard;
}

从vcard生成person

ABAddressBookRef book = ABAddressBookCreate();
ABRecordRef defaultSource = ABAddressBookCopyDefaultSource(book);
CFArrayRef vCardPeople = ABPersonCreatePeopleInSourceWithVCardRepresentation(defaultSource, vCardData);
for (CFIndex index = 0; index < CFArrayGetCount(vCardPeople); index++) {
ABRecordRef person = CFArrayGetValueAtIndex(vCardPeople, index);
ABAddressBookAddRecord(book, person, NULL);
} CFRelease(vCardPeople);
CFRelease(defaultSource);
ABAddressBookSave(book, NULL);
CFRelease(book);

iOS 通讯录编程【总结】的更多相关文章

  1. iOS通讯录相关知识-浅析

    本文来自于:贞娃儿的博客  http://blog.sina.com.cn/zhenwawaer  在开发一些应用中,我们如果需要iPhone设备中的通讯录信息.或者,需要开发通讯录相关的一些功能.那 ...

  2. iOS网络编程模型

    iOS网络编程层次结构也分为三层: Cocoa层:NSURL,Bonjour,Game Kit,WebKit Core Foundation层:基于 C 的 CFNetwork 和 CFNetServ ...

  3. IOS网络编程——第三方类库

    IOS网络编程——第三方类库 目录 概述 ASIHttpRequest AFNetworking 其他 概述 ASIHttpRequest AFNetworking 其他

  4. iOS 6编程Cookbook(影印版)

    <iOS 6编程Cookbook(影印版)> 基本信息 原书名:iOS 6 Programming Cookbook 作者: Vandad Nahavandipoor 出版社:东南大学出版 ...

  5. iOS 并行编程:NSOperation Queues

    1 简介 1.1 功能        Operation Queue也是IOS的一种并行编程技术,类似Dispatch Queue可以帮助用户管理多线程.但是Operation Queue将任务封装在 ...

  6. IOS网络编程:HTTP

    IOS网络编程:HTTP HTTP定义了一种在服务器和客户端之间传递数据的途径. URL定义了一种唯一标示资源在网络中位置的途径. REQUESTS 和 RESPONSES: 客户端先建立一个TCP连 ...

  7. 深入浅出-iOS函数式编程的实现 && 响应式编程概念

    简介 本篇主要回顾一下--iOS函数式编程 && 响应式编程概念 ,如何一步步实现函数式编程的过程,对阅读Masonry && SnapKit源码有一定的帮助. 配图 ...

  8. [HMLY]11.iOS函数式编程的实现&&响应式编程概念

    简介 本篇主要回顾一下--iOS函数式编程 && 响应式编程概念 ,如何一步步实现函数式编程的过程,对阅读Masonry && SnapKit源码有一定的帮助. 作为一 ...

  9. iOS网络编程笔记——Socket编程

    一.什么是Socket通信: Socket是网络上的两个程序,通过一个双向的通信连接,实现数据的交换.这个双向连路的一端称为socket.socket通常用来实现客户方和服务方的连接.socket是T ...

随机推荐

  1. hdu2029

    http://acm.hdu.edu.cn/showproblem.php?pid=2029 #include<stdio.h> #include<string.h> #inc ...

  2. Android 6.0 如何添加完整的系统服务(app-framework-kernel)

    最近学习了如何在Android 6.0上添加一个系统服务,APP如何通过新增的系统服务访问底层驱动.在这学习过程中,收获颇多,并结合学习了<Embeded Android>--Karim ...

  3. Android开发初体验

    本文通过开发一个应用来学习Android基本概念及构成应用的UI组件. 开发的应用名叫GeoQuiz,它能给出一道道地理知识问题.用户点击true或false按钮回答问题,应用即时做出反馈 第一步请先 ...

  4. [Android]异常8-android.view.WindowManager$BadTokenException

    背景:Service服务中使用WindowManager时,Android4.4使用正常,Android6.0使用应用崩溃停止运行,提示android.view.WindowManager$BadTo ...

  5. 从ABC到流利口语-unit01

    Unit 1 Introduction1 Good evening,everyone.It's a pleasure to you all. My name is Wang Dong.I'M from ...

  6. (转) 分布式文件存储FastDFS(一)初识FastDFS

    http://blog.csdn.net/xingjiarong/article/details/50559849 一.FastDFS简介 FastDFS是一款开源的.分布式文件系统(Distribu ...

  7. NSOperationQueue和GCD的区别,以及在什么场合下使用

    1> GCD是纯C语言的API .NSOperationQueue是基于GCD的OC的封装. 2> GCD只支持FIFO队列,NSOperationQueue可以方便设置执行顺序,设置最大 ...

  8. 内网jenkins如何配置gitlab自动拉取代码打包

    在全局工具配置中添加git安装目录的配置 http://10.2.1.92:8080/jenkins/configureTools/git1.8.3.1/usr/bin/git 打开系统设置配置git ...

  9. 生成元(Digit Generator, ACM/ICPC Seoul 2005, UVa1583)

    如果x加上x的各个数字之和得到y,就说x是y的生成元.给出n(1≤n≤100000),求最小 生成元.无解输出0.例如,n=216,121,2005时的解分别为198,0,1979. [分析] 本题看 ...

  10. Chat Group gym101775A(逆元,组合数)

    传送门:Chat Group(gym101775A) 题意:一个宿舍中又n个人,最少k(k >= 3)个人就可以建一个讨论组,问最多可以建多少个不同的讨论组. 思路:求组合数的和,因为涉及除法取 ...