在开发中会遇到应用需要记录设备标示,即使应用卸载后再安装也可重新识别的情况,在这写一种实现方式——读取设备的UUID(Universally Unique Identifier)并通过KeyChain记录。
首先iOS中获取设备唯一标示符的方法一直随版本的更新而变化。iOS 2.0版本以后UIDevice提供一个获取设备唯一标识符的方法uniqueIdentifier,通过该方法我们可以获取设备的序列号,这个也是目前为止唯一可以确认唯一的标示符。好景不长,因为该唯一标识符与手机一一对应,苹果觉得可能会泄露用户隐私,所以在 iOS 5.0之后该方法就被废弃掉了;iOS 6.0系统新增了两个用于替换uniqueIdentifier的接口,分别是:identifierForVendor,advertisingIdentifier,但这两个接口会在应用重新安装时改变数值,并不是唯一的标示符,所以开发者改为使用WiFi的mac地址来取代;iOS 7中苹果又封杀mac地址,所以开发者再次改变思路使用KeyChain来保存获取到的UDID,这样以后即使APP删了再装回来,也可以从KeyChain中读取回来。
//
// YKeychain.h
//
//
// Created by CJW on 17/8/4.
// Copyright © 2017年 onight. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface YKeychain : NSObject
NS_ASSUME_NONNULL_BEGIN
+ (BOOL)setValue:(id)value forKey:(NSString *)key;
+ (BOOL)setValue:(id)value forKey:(NSString *)key forAccessGroup:(nullable NSString *)group;
+ (id)valueForKey:(NSString *)key;
+ (id)valueForKey:(NSString *)key forAccessGroup:(nullable NSString *)group;
+ (BOOL)deleteValueForKey:(NSString *)key;
+ (BOOL)deleteValueForKey:(NSString *)key forAccessGroup:(nullable NSString *)group;
+ (NSString *)getBundleSeedIdentifier;
@end
NS_ASSUME_NONNULL_END
//
// YKeychain
//
//
// Created by CJW on 17/8/4.
// Copyright © 2017年 onight. All rights reserved.
//
#import "YKeychain.h"
@implementation YKeychain
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)key forAccessGroup:(NSString *)group{
NSMutableDictionary *query = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService : key,
(__bridge id)kSecAttrAccount : key,
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAfterFirstUnlock
}.mutableCopy;
if (group != nil) {
[query setObject:[self getFullAccessGroup:group] forKey:(__bridge id)kSecAttrAccessGroup];
}
return query;
}
+ (NSString *)getFullAccessGroup:(NSString *)group
{
NSString *accessGroup = nil;
NSString *bundleSeedIdentifier = [self getBundleSeedIdentifier];
if (bundleSeedIdentifier != nil && [group rangeOfString:bundleSeedIdentifier].location == NSNotFound) {
accessGroup = [NSString stringWithFormat:@"%@.%@", bundleSeedIdentifier, group];
}
return accessGroup;
}
+ (NSString *)getBundleSeedIdentifier
{
static __strong NSString *bundleSeedIdentifier = nil;
if (bundleSeedIdentifier == nil) {
@synchronized(self) {
if (bundleSeedIdentifier == nil) {
NSString *_bundleSeedIdentifier = nil;
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge NSString *)kSecClassGenericPassword,
(__bridge id)kSecAttrAccount: @"bundleSeedID",
(__bridge id)kSecAttrService: @"",
(__bridge id)kSecReturnAttributes: (__bridge id)kCFBooleanTrue
};
CFDictionaryRef result = nil;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
if (status == errSecItemNotFound) {
status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
}
if (status == errSecSuccess) {
NSString *accessGroup = [(__bridge NSDictionary *)result objectForKey:(__bridge NSString *)kSecAttrAccessGroup];
NSArray *components = [accessGroup componentsSeparatedByString:@"."];
_bundleSeedIdentifier = [[components objectEnumerator] nextObject];
CFRelease(result);
}
if (_bundleSeedIdentifier != nil) {
bundleSeedIdentifier = [_bundleSeedIdentifier copy];
}
}
}
}
return bundleSeedIdentifier;
}
+ (BOOL)setValue:(id)value forKey:(NSString *)key{
return [self setValue:value forKey:key forAccessGroup:nil];
}
+ (BOOL)setValue:(id)value forKey:(NSString *)key forAccessGroup:(NSString *)group{
NSMutableDictionary *query = [self getKeychainQuery:key forAccessGroup:group];
[self deleteValueForKey:key forAccessGroup:group];
NSData *data = nil;
@try {
data = [NSKeyedArchiver archivedDataWithRootObject:value];
} @catch (NSException *exception) {
return NO;
}
[query setObject:data forKey:(__bridge id)kSecValueData];
OSStatus result = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
return result == errSecSuccess;
}
+ (BOOL)deleteValueForKey:(NSString *)key{
return [self deleteValueForKey:key forAccessGroup:nil];
}
+ (BOOL)deleteValueForKey:(NSString *)key forAccessGroup:(NSString *)group{
NSMutableDictionary *query = [self getKeychainQuery:key forAccessGroup:group];
OSStatus result = SecItemDelete((__bridge CFDictionaryRef)query);
return result == errSecSuccess;
}
+ (id)valueForKey:(NSString *)key{
return [self valueForKey:key forAccessGroup:nil];
}
+ (id)valueForKey:(NSString *)key forAccessGroup:(NSString *)group{
id value = nil;
NSMutableDictionary *query = [self getKeychainQuery:key forAccessGroup:group];
CFDataRef keyData = NULL;
[query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
[query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
if (SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&keyData) == errSecSuccess) {
@try {
value = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
}
@catch (NSException *e) {
value = nil;
}
}
if (keyData) {
CFRelease(keyData);
}
return value;
}
@end
//
// XCKeyChain.h
//
//
// Created by CJW on 17/8/4.
// Copyright © 2017年 onight. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface XCKeyChain : NSObject
+(void)setKeyUUID:(NSString *)str;
+(NSString*)getKeyUUID;
@end
//
// XCKeyChain.m
// zjfae
//
// Created by CJW on 17/8/4.
// Copyright © 2017年 onight. All rights reserved.
//
#import "XCKeyChain.h"
#import "YKeychain.h"
static NSString * const accessGroup = @"com.zjfae.uuidKey";
@implementation XCKeyChain
+(void)setKeyUUID:(NSString *)str{
[YKeychain setValue:str forKey:accessGroup];
}
+(NSString*)getKeyUUID{
NSString * uuidStr = [YKeychain valueForKey:accessGroup];
if(uuidStr == nil){
return @"";
}else{
return uuidStr;
}
}
@end
/// 保存用户UUID
class func saveUserUUID(str:String){
XCKeyChain.setKeyUUID(str)
}
class func getUserUUID() -> String {
return XCKeyChain.getKeyUUID()
}
参照简书: http://www.jianshu.com/p/354ea1279e68