本文主要介绍使用KeyChain保存和获取APP数据,解决iOS上获取不变UDID的问题。并给出一个获取UDID的工具类,使用方便。
KeyChain介绍
iOS中的KeyChain相比OS X比较简单,整个系统只有一个KeyChain,每个程序都可以往KeyChain中记录数据,而且只能读取到自己程序记录在KeyChain中的数据。iOS中Security.framework框架提供了四个主要的方法来操作KeyChain:
// 查询
OSStatus SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result);
// 添加
OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result);
// 更新KeyChain中的Item
OSStatus SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
// 删除KeyChain中的Item
OSStatus SecItemDelete(CFDictionaryRef query)
这四个方法参数比较复杂,一旦传错就会导致操作KeyChain失败,这块儿文档中介绍的比较详细,大家可以查查官方文档Keychain Services Reference。
前面提到了每个APP只允许访问自己在KeyChain中记录的数据,那么是不是就没有别的办法访问其他APP存在KeyChain的数据了?
苹果提供了一个方法允许同一个发商的多个APP访问各APP之间的途径,即在调SecItemAdd添加数据的时候指定AccessGroup,即访问组。一个APP可以同时属于多个分组,添加KeyChain数据访问组等一些列的内容不在此赘述,本文主要讲解iOS上获取不变UDID的问题。
使用KeyChain保存和获取UDID
YXYKeyChainManager.h
//
// YXYKeyChainManager.h
// PrivilegeGo
//
// Created by YXY on 2017/7/13.
// Copyright © 2017年 Techwis. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface YXYKeyChainManager : NSObject
// 存储
+ (void)save:(NSString *)service data:(id)data;
// 查询
+ (id)load:(NSString *)service;
// 删除
+ (void)deleteKeyData:(NSString *)service;
@end
YXYKeyChainManager.m
//
// YXYKeyChainManager.m
// PrivilegeGo
//
// Created by YXY on 2017/7/13.
// Copyright © 2017年 Techwis. All rights reserved.
//
#import "YXYKeyChainManager.h"
@implementation YXYKeyChainManager
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassGenericPassword,(id)kSecClass,
service, (id)kSecAttrService,
service, (id)kSecAttrAccount,
(id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
nil];
}
+ (void)save:(NSString *)service data:(id)data {
//Get search dictionary
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Delete old item before add new item
SecItemDelete((CFDictionaryRef)keychainQuery);
//Add new object to search dictionary(Attention:the data format)
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
//Add item to keychain with the search dictionary
SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
}
+ (id)load:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Configure the search setting
//Since in our simple case we are expecting only a single at tribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
[keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
@try {
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
} @catch (NSException *e) {
NSLog(@"Unarchive of %@ failed: %@", service, e);
} @finally {
}
}
if (keyData)
CFRelease(keyData);
return ret;
}
+ (void)deleteKeyData:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((CFDictionaryRef)keychainQuery);
}
@end