iOS 13.x获取通讯录信息时的注意事项
之前的项目兼容的版本在iOS9.0以上,所以在项目中使用了CNContacts框架获取用户的通讯录信息,大致的实现如下:
/// 判断字符串是否可用
/// 辅助函数
/// @param str 原始对象
BOOL isAvailableString(NSString *str) {if ([str isKindOfClass:[NSNull class]]) {return false;}if (!str || !str.length) {return false;}return true;
}/// 查看通讯录访问权限
void getAuthrization(void(^_Nonnull result)(BOOL granted)) {CNAuthorizationStatus authorizationStatus = [CNContactStore authorizationStatusForEntityType:(CNEntityTypeContacts)];if (authorizationStatus == CNAuthorizationStatusNotDetermined) {CNContactStore *contactStore = [[CNContactStore alloc] init];[contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {if (result) {result(granted);}}];} else if (authorizationStatus == CNAuthorizationStatusAuthorized){if (result) {result(true);}} else {if (result) {result(true);}}
}/// 获取通讯录信息
/// @param result 结果回调
- (void)getAllContacts:(void(^)(NSArray *))result {getAuthrization(^(BOOL granted){if (granted) {CNContactStore *contactStore = [[CNContactStore alloc] init];CNContactFetchRequest *fetchRequest = [[CNContactFetchRequest alloc] initWithKeysToFetch:@[CNContactFamilyNameKey, CNContactGivenNameKey, CNContactMiddleNameKey, CNContactNoteKey]];NSError *error = nil;__block NSMutableArray <NSDictionary<NSString *, NSString *> *> *allContacts = @[].mutableCopy;[contactStore enumerateContactsWithFetchRequest:fetchRequest error:&error usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {NSString *familyName = contact.familyName;familyName = isAvailableString(familyName) ? familyName : @"";NSString *middleName = contact.middleName;middleName = isAvailableString(middleName) ? middleName : @"";NSString *givenName = contact.givenName;givenName = isAvailableString(givenName) ? givenName : @"";NSString *note = contact.note;note = isAvailableString(note) ? note : @"";NSString *fullName = [NSString stringWithFormat:@"%@%@%@", familyName, middleName, givenName];if (isAvailableString(fullName)) {[allContacts addObject:@{@"fullName" : fullName, @"note" : note}];}}];if (result) {result(allContacts);}}});
}
一直相安无事,正常运行.直到某一天早上突然发现,预警系统相当一部分用户的所有的联系人信息一夜之间全部为空,一脸懵逼.
其实在早前的发布大会上,已经说明了要对要用户通讯录隐私数据的使用权限做限制,尤其是"备注"信息,可能会涉及到用户的个人私密数据,例如银行卡密码,亲密人物关系等,所以在iOS13.x上使用用户通讯录数据时就需要注意.
在iOS 13.x 上获取用户的通讯录数据时,不能获取 "备注" 信息,否则会系统会抛出异常,从而导致获取信息失败.在获取的方法中,修改一下实现来查看一下是否有异常发生:
/// 获取通讯录信息
/// @param result 结果回调
- (void)getAllContacts:(void(^)(NSArray *))result {getAuthrization(^(BOOL granted){if (granted) {CNContactStore *contactStore = [[CNContactStore alloc] init];CNContactFetchRequest *fetchRequest = [[CNContactFetchRequest alloc] initWithKeysToFetch:@[CNContactFamilyNameKey, CNContactGivenNameKey, CNContactMiddleNameKey, CNContactNoteKey]];NSError *error = nil;__block NSMutableArray <NSDictionary<NSString *, NSString *> *> *allContacts = @[].mutableCopy;BOOL fetchSuccess = [contactStore enumerateContactsWithFetchRequest:fetchRequest error:&error usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {NSString *familyName = contact.familyName;familyName = isAvailableString(familyName) ? familyName : @"";NSString *middleName = contact.middleName;middleName = isAvailableString(middleName) ? middleName : @"";NSString *givenName = contact.givenName;givenName = isAvailableString(givenName) ? givenName : @"";NSString *note = contact.note;note = isAvailableString(note) ? note : @"";NSString *fullName = [NSString stringWithFormat:@"%@%@%@", familyName, middleName, givenName];if (isAvailableString(fullName)) {[allContacts addObject:@{@"fullName" : fullName, @"note" : note}];}}];if (!fetchSuccess || error) {NSLog(@"fetchSuccess : %@", fetchSuccess ? @"成功" : @"失败");NSLog(@"error : %@", error);}if (result) {result(allContacts);}}});
}
再次运行,输出信息果然是异常的:
fetchSuccess : 失败error : Error Domain=CNErrorDomain Code=102 "(null)" UserInfo={CNKeyPaths=(note
), CNInvalidRecords=("<CNContactFetchRequest: 0x281ac7030: predicate=(null), keysToFetch=(\n familyName,\n givenName,\n middleName,\n note\n), unifyResults=1, sortOrder=0>"
)}
然后在CNErrorDomain定义中找到了关于Code=102的说明:
CONTACTS_EXTERN NSString * const CNErrorDomain NS_AVAILABLE(10_11, 9_0);typedef NS_ENUM(NSInteger, CNErrorCode)
{CNErrorCodeCommunicationError = 1,CNErrorCodeDataAccessError = 2,CNErrorCodeAuthorizationDenied = 100,CNErrorCodeNoAccessableWritableContainers NS_ENUM_AVAILABLE(10_13_3, 11_3) = 101,CNErrorCodeUnauthorizedKeys API_AVAILABLE(macos(10.14), ios(13.0), watchos(6.0)) = 102,CNErrorCodeFeatureDisabledByUser API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0)) = 103,CNErrorCodeRecordDoesNotExist = 200,CNErrorCodeInsertedRecordAlreadyExists = 201,CNErrorCodeContainmentCycle = 202,CNErrorCodeContainmentScope = 203,CNErrorCodeParentRecordDoesNotExist = 204,CNErrorCodeRecordIdentifierInvalid = 205,CNErrorCodeRecordNotWritable API_AVAILABLE(macos(10.14), ios(13.0), watchos(6.0)) = 206,CNErrorCodeParentContainerNotWritable API_AVAILABLE(macos(10.14), ios(13.0), watchos(6.0)) = 207,CNErrorCodeValidationMultipleErrors = 300,CNErrorCodeValidationTypeMismatch = 301,CNErrorCodeValidationConfigurationError = 302,CNErrorCodePredicateInvalid = 400,CNErrorCodePolicyViolation = 500,CNErrorCodeClientIdentifierInvalid = 600,CNErrorCodeClientIdentifierDoesNotExist = 601,CNErrorCodeClientIdentifierCollision = 602,CNErrorCodeChangeHistoryExpired = 603,CNErrorCodeChangeHistoryInvalidAnchor = 604,CNErrorCodeVCardMalformed NS_ENUM_AVAILABLE(10_13, 11_0) = 700,CNErrorCodeVCardSummarizationError NS_ENUM_AVAILABLE(10_14, 12_0) = 701,} NS_ENUM_AVAILABLE(10_11, 9_0);
发现CNErrorCodeUnauthorizedKeys = 102是获取了未授权的属性key.由此真相大白,苹果爸爸不再授权获取note属性了.那么我是不是只要在CNContactFetchRequest初始化时,不加入CNContactNoteKey就可以了,大不了在访问时获取不到数据就完事了?
CNContactStore *contactStore = [[CNContactStore alloc] init];
CNContactFetchRequest *fetchRequest = [[CNContactFetchRequest alloc] initWithKeysToFetch:@[CNContactFamilyNameKey, CNContactGivenNameKey, CNContactMiddleNameKey, /*CNContactNoteKey*/]];
再次运行,结果
Ignored Exception: A property was not requested when contact was fetched.
(0 CoreFoundation 0x00000001a2f49c44 97285ACB-7B21-393A-ABF6-03F1DBB5D2A2 + 12565161 libobjc.A.dylib 0x00000001a2c640c8 objc_exception_throw + 602 CoreFoundation 0x00000001a2e393ac 97285ACB-7B21-393A-ABF6-03F1DBB5D2A2 + 1402043 Contacts 0x00000001aa321d18 75694259-6C2D-34A1-828B-E0A506C23F35 + 8184564 ContactsDemo 0x0000000100a91528 __33-[ViewController getAllContacts:]_block_invoke.83 + 4125 Contacts 0x00000001aa2c0434 75694259-6C2D-34A1-828B-E0A506C23F35 + 4188686 Contacts 0x00000001aa2f5a08 75694259-6C2D-34A1-828B-E0A506C23F35 + 6374487 CoreFoundation 0x00000001a2f1b7c4 97285ACB-7B21-393A-ABF6-03F1DBB5D2A2 + 10669488 CoreFoundation 0x00000001a2e19424 97285ACB-7B21-393A-ABF6-03F1DBB5D2A2 + 92529 Contacts 0x00000001aa2f5914 75694259-6C2D-34A1-828B-E0A506C23F35 + 63720410 ContactsFoundation 0x00000001ac8ac78c CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 17806011 ContactsFoundation 0x00000001ac8e4b24 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 40835612 ContactsFoundation 0x00000001ac8e4b24 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 40835613 ContactsFoundation 0x00000001ac8e4b24 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 40835614 ContactsFoundation 0x00000001ac8e4b24 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 40835615 ContactsFoundation 0x00000001ac8e4b24 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 40835616 Contacts 0x00000001aa2881f8 75694259-6C2D-34A1-828B-E0A506C23F35 + 18892017 Contacts 0x00000001aa2888b8 75694259-6C2D-34A1-828B-E0A506C23F35 + 19064818 Contacts 0x00000001aa289024 75694259-6C2D-34A1-828B-E0A506C23F35 + 19254819 CoreFoundation 0x00000001a2f50724 97285ACB-7B21-393A-ABF6-03F1DBB5D2A2 + 128387620 CoreFoundation 0x00000001a2e19280 97285ACB-7B21-393A-ABF6-03F1DBB5D2A2 + 883221 Foundation 0x00000001a34489cc 672CF0CB-4951-3B91-89DF-55E953AEA00F + 241095622 Foundation 0x00000001a3211088 672CF0CB-4951-3B91-89DF-55E953AEA00F + 8615223 Foundation 0x00000001a3449d04 672CF0CB-4951-3B91-89DF-55E953AEA00F + 241587624 Foundation 0x00000001a32102d4 672CF0CB-4951-3B91-89DF-55E953AEA00F + 8264425 Foundation 0x00000001a321e0ac 672CF0CB-4951-3B91-89DF-55E953AEA00F + 13943626 Foundation 0x00000001a3450f44 672CF0CB-4951-3B91-89DF-55E953AEA00F + 244512427 Contacts 0x00000001aa288c78 75694259-6C2D-34A1-828B-E0A506C23F35 + 19160828 Contacts 0x00000001aa288738 75694259-6C2D-34A1-828B-E0A506C23F35 + 19026429 Contacts 0x00000001aa288060 75694259-6C2D-34A1-828B-E0A506C23F35 + 18851230 ContactsFoundation 0x00000001ac8c6780 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 28454431 ContactsFoundation 0x00000001ac8abb08 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 17485632 ContactsFoundation 0x00000001ac8c6780 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 28454433 ContactsFoundation 0x00000001ac8ab800 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 17408034 ContactsFoundation 0x00000001ac8c6780 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 28454435 ContactsFoundation 0x00000001ac8ab800 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 17408036 ContactsFoundation 0x00000001ac8c6780 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 28454437 ContactsFoundation 0x00000001ac8abb08 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 17485638 ContactsFoundation 0x00000001ac8c6780 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 28454439 ContactsFoundation 0x00000001ac8ab45c CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 17314840 ContactsFoundation 0x00000001ac8c6780 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 28454441 ContactsFoundation 0x00000001ac8ac638 CAD1A4FE-E7C1-3980-8391-9E6E7CA88370 + 17772042 Contacts 0x00000001aa2f55d4 75694259-6C2D-34A1-828B-E0A506C23F35 + 63637243 Contacts 0x00000001aa3584d0 75694259-6C2D-34A1-828B-E0A506C23F35 + 104161644 libsystem_trace.dylib 0x00000001a2b27d30 os_activity_apply_f + 8845 Contacts 0x00000001aa358438 75694259-6C2D-34A1-828B-E0A506C23F35 + 104146446 Contacts 0x00000001aa2f52a4 75694259-6C2D-34A1-828B-E0A506C23F35 + 63555647 Contacts 0x00000001aa2c0320 75694259-6C2D-34A1-828B-E0A506C23F35 + 41859248 ContactsDemo 0x0000000100a91180 __33-[ViewController getAllContacts:]_block_invoke + 48049 ContactsDemo 0x0000000100a90dbc getAuthrization + 34050 ContactsDemo 0x0000000100a90f7c -[ViewController getAllContacts:] + 12051 ContactsDemo 0x0000000100a917f0 -[ViewController tableView:didSelectRowAtIndexPath:] + 10452 UIKitCore 0x00000001a71b04c0 55D60569-DBB7-3BC9-8A97-DCAC95C64D4B + 1258822453 UIKitCore 0x00000001a71afff8 55D60569-DBB7-3BC9-8A97-DCAC95C64D4B + 1258700054 UIKitCore 0x00000001a71b06e0 55D60569-DBB7-3BC9-8A97-DCAC95C64D4B + 1258876855 UIKitCore 0x00000001a6fe9078 55D60569-DBB7-3BC9-8A97-DCAC95C64D4B + 1072344856 UIKitCore 0x00000001a6fd876c 55D60569-DBB7-3BC9-8A97-DCAC95C64D4B + 1065559657 UIKitCore 0x00000001a70099a0 55D60569-DBB7-3BC9-8A97-DCAC95C64D4B + 1085686458 CoreFoundation 0x00000001a2ec467c 97285ACB-7B21-393A-ABF6-03F1DBB5D2A2 + 71026859 CoreFoundation 0x00000001a2ebf31c 97285ACB-7B21-393A-ABF6-03F1DBB5D2A2 + 68892460 CoreFoundation 0x00000001a2ebf8cc 97285ACB-7B21-393A-ABF6-03F1DBB5D2A2 + 69038061 CoreFoundation 0x00000001a2ebf098 CFRunLoopRunSpecific + 48062 GraphicsServices 0x00000001ad029534 GSEventRunModal + 10863 UIKitCore 0x00000001a6fdf7ac UIApplicationMain + 194064 ContactsDemo 0x0000000100a92cb8 main + 12465 libdyld.dylib 0x00000001a2d3ef30 0DC9A4BA-C3E8-3487-99DB-1B5C86597AF5 + 3888
)
尴尬....就连之前设定的日志
if (!fetchSuccess || error) {NSLog(@"fetchSuccess : %@", fetchSuccess ? @"成功" : @"失败");NSLog(@"error : %@", error);}
都没有打印,但是通过断点,那就说明fetchSuccess返回了成功,同时error为空,这种隐藏的异常是最难发现的.通过设置断点发现获取的遍历方法还是执行了的.为啥会这样呢,我们使用异常捕获来查看一下:
@try {NSString *familyName = contact.familyName;familyName = isAvailableString(familyName) ? familyName : @"";NSString *middleName = contact.middleName;middleName = isAvailableString(middleName) ? middleName : @"";NSString *givenName = contact.givenName;givenName = isAvailableString(givenName) ? givenName : @"";NSString *note = contact.note;note = isAvailableString(note) ? note : @"";NSString *fullName = [NSString stringWithFormat:@"%@%@%@", familyName, middleName, givenName];if (isAvailableString(fullName)) {[allContacts addObject:@{@"fullName" : fullName, @"note" : note}];}} @catch (NSException *exception) {NSLog(@"exception == %@", exception);} @finally {}
然后发现:
exception == A property was not requested when contact was fetched.
exception == A property was not requested when contact was fetched.
...
所以,既然苹果粑粑不让用,在正常情况下就不要试图去申请,也不要视图去获取,因为你粑粑终究是你粑粑,如果非要不听话,那就要准备好背锅.然后删掉
contact.note
调用,代码正常运行.
最后我们可以在此处添加异常检测代码,以防止以后此处再次发生改动时可以及时找到原因.
- (void)getAllContacts:(void(^)(NSArray *))result {getAuthrization(^(BOOL granted){if (granted) {CNContactStore *contactStore = [[CNContactStore alloc] init];CNContactFetchRequest *fetchRequest = [[CNContactFetchRequest alloc] initWithKeysToFetch:@[CNContactFamilyNameKey, CNContactGivenNameKey, CNContactMiddleNameKey, /*CNContactNoteKey*/]];NSError *error = nil;__block NSMutableArray <NSDictionary<NSString *, NSString *> *> *allContacts = @[].mutableCopy;BOOL fetchSuccess = [contactStore enumerateContactsWithFetchRequest:fetchRequest error:&error usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {@try {NSString *familyName = contact.familyName;familyName = isAvailableString(familyName) ? familyName : @"";NSString *middleName = contact.middleName;middleName = isAvailableString(middleName) ? middleName : @"";NSString *givenName = contact.givenName;givenName = isAvailableString(givenName) ? givenName : @"";NSString *note = /*contact.note*/@"";note = isAvailableString(note) ? note : @"";NSString *fullName = [NSString stringWithFormat:@"%@%@%@", familyName, middleName, givenName];if (isAvailableString(fullName)) {[allContacts addObject:@{@"fullName" : fullName, @"note" : note}];}} @catch (NSException *exception) {NSLog(@"exception == %@", exception);//添加异常检测[WEDExceptionManager sendExcetion:@"ContactEnumerateException" description:exception.description];} @finally {}}];if (!fetchSuccess || error) {//添加异常检测[WEDExceptionManager sendExcetion:@"ContactEnumerateException" description:error ? error.description : @"" ];}if (result) {result(allContacts);}}});
}
iOS 13.x获取通讯录信息时的注意事项相关推荐
- ios 简书 获取通讯录信息_iOS 13获取用户通讯录需要注意的
获取通讯录还有一个最大的变化,就是iOS9以后 访问通讯录属性,要事先设置好,否则会奔溃, 下面是列表 // 姓名前缀 CNContactNamePrefixKey // 名 CNContactGiv ...
- ios 简书 获取通讯录信息_当心!你用的共享充电宝可能被植入木马盗取信息
出门在外,你的焦虑感是否会随着手机电量的降低而同步提高呢?乘车.支付.娱乐.联系都离不开手机,一旦没了电简直要丢掉半条命.这个时候遍布大街小巷的共享充电宝简直成了救命稻草.然而在你放心使用共享充电宝时 ...
- ios 简书 获取通讯录信息_iOS9以后获取手机通讯录
一.iOS 9 以前的通讯录框架 AddressBookUI框架:提供了联系人列表界面.联系人详情界面.添加联系人界面等,一般用于选择联系人. AddressBook 框架:纯 C 语言的 API,仅 ...
- ios 简书 获取通讯录信息_ios 各种权限整理
联网权限 引入头文件 @import CoreTelephony; 应用启动后,检测应用中是否有联网权限 CTCellularData *cellularData = [[CTCellularData ...
- ios 简书 获取通讯录信息_iOS-授权获取通讯录
- (void)getContact{ CNAuthorizationStatus authorizationStatus = [CNContactStore authorizationStatusF ...
- ios通过app读取通讯录信息
iOS通过app读取通讯录信息,读取通讯录信息时需要加载AddressBookUI 和AddressBook两个包,并且引入头文件 #import <AddressBook/AddressBoo ...
- 【微信公众号开发】获取用户信息时,有时成功获取,有时提示“invalid openid hint”
原因:保存openid的session在某个方法里被重新赋新值了.所以一旦调用了这个方法,再去获取用户信息时,就会报错.
- Android获取通讯录信息
Android获取通讯录信息 欢迎关注本人网站:rytter的私人博客网站 权限申请 自Android6.0以后,Android权限申请全部要求为动态申请(注:通讯录权限为危险权限),权限申请方法如下 ...
- iOS开发编码规范与审核时的注意事项
iOS开发编码规范与审核时的注意事项 一. 编码的排版格式 1 代码的缩进应使用空格(SPACE),不能使用制表符(TAB),并且缩进以2个字符为单位. 2 空格的使用 a) 关键字与其后的表达式之间 ...
最新文章
- CF853 (Div.1) A Planning(贪心)
- ⑤Windows Server 8 RemoteFX体验
- vue1与vue2的路由 以及vue2项目大概了解
- 2.2 逻辑回归-机器学习笔记-斯坦福吴恩达教授
- String字符串中获取所有匹配结果的索引值
- spring security config
- 制作 Windows8 to Go
- mysql 分组查出来横向展示_实战MySql查询
- Java刷新Jpanel_java – 刷新JPanel
- (11) python 使用baostock获取历史A股K线数据
- 耿楠《LaTeX 中文教程》随堂学习记录
- 软件工程__CMMCMMI
- safari浏览器关于时间日期格式的兼容问题
- 蓝牙、wifi、3G/4G、lora技术的对比
- cannot find -l****问题的解决办法
- 青岛科技大学计算机一级报名,2019年3月山东青岛科技大学计算机等级考试报名通知...
- macbook 如何稳定的使用第三方鼠标
- 串行通信基础、串行数据传输方式
- 她只用一个方法,就把英语拿下了!
- mysql生产cdm文件_PowerDesigner生成CDM模型
热门文章
- 达人评测 华为MatePad2和华为MatePad2 Pro 怎么样
- HP3777打印机一行清晰一行不清晰
- 总结:IOTA反驳DCI实验室提出的漏洞荒谬至极
- 如何实现数据库的读写分离
- python提取cad坐标_教你一个CAD坐标提取的小技巧
- OpenJudge-005雇佣兵(解析)
- android微信登录的app签名 和 项目正式签名替换默认签名
- Callnovo如何因小见大,高端定制——“快乐的音符跳动在异国他乡”篇
- android横屏分辨率,android适配不同分辨率hdpi、mdpi、ldpi横屏竖屏
- 在云计算行业内有什么证书比较有名?受市场认可大?