本文主要介紹使用KeyChain保存和獲取APP數(shù)據(jù)籽御,解決iOS上獲取不變UDID的問題肢执。并給出一個獲取UDID的工具類,使用方便译红。
KeyChain介紹
iOS中的KeyChain相比OS X比較簡單预茄,整個系統(tǒng)只有一個KeyChain,每個程序都可以往KeyChain中記錄數(shù)據(jù)侦厚,而且只能讀取到自己程序記錄在KeyChain中的數(shù)據(jù)耻陕。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)
這四個方法參數(shù)比較復(fù)雜,一旦傳錯就會導(dǎo)致操作KeyChain失敗刨沦,這塊兒文檔中介紹的比較詳細(xì)霜瘪,大家可以查查官方文檔Keychain Services Reference羞秤。
前面提到了每個APP只允許訪問自己在KeyChain中記錄的數(shù)據(jù),那么是不是就沒有別的辦法訪問其他APP存在KeyChain的數(shù)據(jù)了??
蘋果提供了一個方法允許同一個發(fā)商的多個APP訪問各APP之間的途徑,即在調(diào)SecItemAdd添加數(shù)據(jù)的時候指定AccessGroup萎胰,即訪問組。一個APP可以同時屬于多個分組,添加KeyChain數(shù)據(jù)訪問組等一些列的內(nèi)容不在此贅述忘古,本文主要講解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