(绝对有用)iOS获取UUID,并使用keychain存储

Wesley13
• 阅读 446

UDID被弃用,使用UUID来作为设备的唯一标识。获取到UUID后,如果用NSUserDefaults存储,当程序被卸载后重装时,再获得的UUID和之前就不同了。使用keychain存储可以保证程序卸载重装时,UUID不变。但当刷机或者升级系统后,UUID还是会改变的。但这仍是目前为止最佳的解决办法了,如果有更好的解决办法,欢迎留言。 (我整理的解决办法的参考来源:http://blog.k-res.net/archives/1081.html) 给大家两个类:

UUID.h中的代码:

#import 尖括号(Foundation/Foundation.h)

@interface UUID : NSObject

+(NSString *)getUUID;

@end

UUID.m中的代码:

#import "UUID.h" #import 尖括号(Foundation/Foundation.h) #import "KeychainItemWrapper.h"

@implementation UUID

+(NSString *)getUUID { KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc]

                                initWithIdentifier:@"UUID"
                                   
                                                                             accessGroup:@"YOUR_BUNDLE_SEED.com.yourcompany.userinfo"];


NSString *strUUID = [keychainItem objectForKey:(id)CFBridgingRelease(kSecValueData)];

//首次执行该方法时,uuid为空
if ([strUUID isEqualToString:@""])

{
    CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);

    strUUID = (NSString *)CFBridgingRelease(CFUUIDCreateString (kCFAllocatorDefault,uuidRef));

    [keychainItem setObject:strUUID forKey:(id)CFBridgingRelease(kSecValueData)];

}
return strUUID;

}

@end

第二个类是苹果官方的一个Demo里封装的,Demo的下载地址:http://developer.apple.com/library/ios/#samplecode/GenericKeychain/Listings/Classes_KeychainItemWrapper_h.html#//apple_ref/doc/uid/DTS40007797-Classes_KeychainItemWrapper_h-DontLinkElementID_9

KeychainItemWrapper.h中的代码:

#import 尖括号(UIKit/UIKit.h)

@interface KeychainItemWrapper : NSObject { NSMutableDictionary *keychainItemData; NSMutableDictionary *genericPasswordQuery; }

@property (nonatomic, retain) NSMutableDictionary *keychainItemData; @property (nonatomic, retain) NSMutableDictionary *genericPasswordQuery;

  • (id)initWithIdentifier: (NSString *)identifier accessGroup:(NSString *) accessGroup;
  • (void)setObject:(id)inObject forKey:(id)key;
  • (id)objectForKey:(id)key;
  • (void)resetKeychainItem;

@end

KeychainItemWrapper.m中的代码:

#import "KeychainItemWrapper.h" #import 尖括号(Security/Security.h)

@interface KeychainItemWrapper (PrivateMethods)

  • (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert;

  • (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert;

  • (void)writeToKeychain;

@end

@implementation KeychainItemWrapper

@synthesize keychainItemData, genericPasswordQuery;

  • (id)initWithIdentifier: (NSString *)identifier accessGroup:(NSString *) accessGroup; { if (self = [super init]) { genericPasswordQuery = [[NSMutableDictionary alloc] init];

[genericPasswordQuery setObject:(id)CFBridgingRelease(kSecClassGenericPassword) forKey:(id)kSecClass]; [genericPasswordQuery setObject:identifier forKey:(id)CFBridgingRelease(kSecAttrGeneric)];

if (accessGroup != nil) { #if TARGET_IPHONE_SIMULATOR

#else [genericPasswordQuery setObject:accessGroup forKey:(id)kSecAttrAccessGroup]; #endif }

    [genericPasswordQuery setObject:(id)CFBridgingRelease(kSecMatchLimitOne) forKey:(id)kSecMatchLimit];
    [genericPasswordQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
    
    NSDictionary *tempQuery = [NSDictionary dictionaryWithDictionary:genericPasswordQuery];
    
    NSMutableDictionary *outDictionary = nil;
    
    if (! SecItemCopyMatching((CFDictionaryRef)tempQuery, (CFTypeRef *)&outDictionary) == noErr)
    {
        
        [self resetKeychainItem];

[keychainItemData setObject:identifier forKey:(id)kSecAttrGeneric]; if (accessGroup != nil) { #if TARGET_IPHONE_SIMULATOR

#else [keychainItemData setObject:accessGroup forKey:(id)kSecAttrAccessGroup]; #endif } } else { self.keychainItemData = [self secItemFormatToDictionary:outDictionary]; }

[outDictionary release]; }

return self; }

  • (void)dealloc { [keychainItemData release]; [genericPasswordQuery release];

[super dealloc]; }

  • (void)setObject:(id)inObject forKey:(id)key { if (inObject == nil) return; id currentObject = [keychainItemData objectForKey:key]; if (![currentObject isEqual:inObject]) { [keychainItemData setObject:inObject forKey:key]; [self writeToKeychain]; } }

  • (id)objectForKey:(id)key { return [keychainItemData objectForKey:key]; }

  • (void)resetKeychainItem { OSStatus junk = noErr; if (!keychainItemData) { self.keychainItemData = [[NSMutableDictionary alloc] init]; } else if (keychainItemData) { NSMutableDictionary *tempDictionary = [self dictionaryToSecItemFormat:keychainItemData]; junk = SecItemDelete((CFDictionaryRef)tempDictionary); NSAssert( junk == noErr || junk == errSecItemNotFound, @"Problem deleting current dictionary." ); }

    [keychainItemData setObject:@"" forKey:(id)kSecAttrAccount]; [keychainItemData setObject:@"" forKey:(id)kSecAttrLabel]; [keychainItemData setObject:@"" forKey:(id)kSecAttrDescription]; [keychainItemData setObject:@"" forKey:(id)kSecValueData]; }

  • (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert { NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert]; [returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; NSString *passwordString = [dictionaryToConvert objectForKey:(id)kSecValueData]; [returnDictionary setObject:[passwordString dataUsingEncoding:NSUTF8StringEncoding] forKey:(id)kSecValueData];

    return returnDictionary; }

  • (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert { NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert]; [returnDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; [returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; NSData *passwordData = NULL; if (SecItemCopyMatching((CFDictionaryRef)returnDictionary, (CFTypeRef *)&passwordData) == noErr) { [returnDictionary removeObjectForKey:(id)kSecReturnData]; NSString *password = [[[NSString alloc] initWithBytes:[passwordData bytes] length:[passwordData length] encoding:NSUTF8StringEncoding] autorelease]; [returnDictionary setObject:password forKey:(id)kSecValueData]; } else { NSAssert(NO, @"Serious error, no matching item found in the keychain.\n"); }

    [passwordData release];

return returnDictionary; }

  • (void)writeToKeychain { NSDictionary *attributes = NULL; NSMutableDictionary *updateItem = NULL; OSStatus result;

    if (SecItemCopyMatching((CFDictionaryRef)genericPasswordQuery, (CFTypeRef *)&attributes) == noErr) { updateItem = [NSMutableDictionary dictionaryWithDictionary:attributes]; [updateItem setObject:[genericPasswordQuery objectForKey:(id)kSecClass] forKey:(id)kSecClass]; NSMutableDictionary *tempCheck = [self dictionaryToSecItemFormat:keychainItemData]; [tempCheck removeObjectForKey:(id)kSecClass];

#if TARGET_IPHONE_SIMULATOR

[tempCheck removeObjectForKey:(id)kSecAttrAccessGroup]; #endif result = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)tempCheck); NSAssert( result == noErr, @"Couldn't update the Keychain Item." ); } else { result = SecItemAdd((CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL); NSAssert( result == noErr, @"Couldn't add the Keychain Item." ); } }

@end

PS: 1. KeychainItemWrapper类在官方Demo里面也有,如果我复制出错了,大家可以在上面的链接上下载官方Demo. 2.使用时要添加Security.framework 3. 尤为注意的是,UUID类下面这句代码中group的设置方法。 KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc]

                                     initWithIdentifier:@"UUID"
                                   
                                     accessGroup:@"YOUR_BUNDLE_SEED.com.yourcompany.userinfo"];
(1)在项目相同的目录下创建KeychainAccessGroups.plist文件。
     (绝对有用)iOS获取UUID,并使用keychain存储

该文件的结构中最顶层的节点必须是一个名为“keychain-access-groups”的Array,并且该Array中每一项都是一个描述分组的NSString。YOUR_BUNDLE_SEED.com.yourcompany.userinfo就是要设置的组名。 (2)在项目相同的目录下创建KeychainAccessGroups.plist文件。在Target-Build Settings-Code Signing栏下的Code Signing Entitlements右侧添加KeychainAccessGroups.plist,如下图。 (绝对有用)iOS获取UUID,并使用keychain存储

到此,工作就完成了。 首次安装程序时,打印出一个uuid,当把程序卸载后,再用getUUID获得 uuid,打印出来的结果和之前相同。证明达到目的。

测试代码:

NSString * uuid= [UUID getUUID]; NSLog(@"uuid=%@",uuid); 测试结果: uuid=19AAB430-9CB8-4325-ACC5-D7D386B68960

点赞
收藏
评论区
推荐文章
光头强的博客 光头强的博客
2个月前
Java面向对象试题
1、 请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。 创建一个接口A,接口里有一个抽象方法fly()。创建一个Bird类继承Animal类并实现 接口A里的方法输出一条有语句“鸟儿飞翔”,重写eat()方法输出一条语句“鸟儿 吃虫”。在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方
速看!今天我才知道,UUID还分五个版本
通用唯一识别码(英语:Universally Unique Identifier,缩写:UUID)是用于计算机(https://zh.wikiped
Wesley13 Wesley13
1年前
MySQL数据库表设计规范
#### **一、数据库设计** 1、一般都使用 INNODB 存储引擎,除非读写比率<1%,才考虑使用 MYISAM 存储引擎;其 他存储引擎请在 DBA 的建议下使用。 2、Stored procedure (包括存储过程,函数,触发器)对于 MYSQL 来说还不是很成熟, 没有完善的出错记录处理,不建议使用。 3、UUID(),USER()这样的
Wesley13 Wesley13
1年前
VirtualBox导入已安装好的操作系统的方法
VirtualBox导入已安装好的操作系统的方法 ------------------------ **1、修改UUID** 进入VirtualBox安装目录,运行VBoxManage修改UUID,命令运行如下: D:\\VirtualBox>VBoxManage.exe internalcommands setvdiuuid E:\\VirtualX
Stella981 Stella981
1年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置 1、virsh edit centos7 找到“memory”和“vcpu”标签,将 <name>centos7</name> <uuid>2220a6d1-a36a-4fbb-8523-e078b3dfe795</uuid>
Easter79 Easter79
1年前
Twitter的分布式自增ID算法snowflake (Java版)
概述 == 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。 有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。 而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
1年前
Android蓝牙连接汽车OBD设备
//设备连接 public class BluetoothConnect implements Runnable {     private static final UUID CONNECT_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
helloworld_34035044 helloworld_34035044
4个月前
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。 uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid() 或 uuid(sep)参数说明:sep 布尔值,生成的uuid中是否包含分隔符'',缺省为
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞 # 背景 在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究