iOS 获取设备唯一识别码 IDFV+keychain

最近项目中需要获取到设备的唯一标志传送给后台存储备用,在UDID UUID IDFA等都存在诸多问题(什么问题可以自己查阅资料)的情况下,选择了 IDFV+keychain(当然特殊情况下也存在些许问题,但基本无影响,非常够用)。

identifierForVendor是apple给供应商唯一的一个值,也就是说同一个公司发行的的app在相同的设备上运行的时候会有这个相同的标识符。然而,如果用户删除了这个供应商的所有app然后再重新安装的话,这个标识符就会不一致。所以要结合keychain使用,在第一次使用App的时候把 IDFV存到keychain中,以后即使APP写在重装,我们只需要从keychain中取跟我们APP相关联的IDFV值就可以了,这样既保证了唯一性有保证了持久性。

废话不多说,下面直接来说怎么实现!

 IDFV的获取方法:

NSString *identifierForVendor = [[UIDevice currentDevice].identifierForVendor UUIDString]; 
我们现在把 IDFV+keychain封装成一个工具类:

新建一个继承自NSObject的类 IDFVTools 用来封装我们的IDFV+keychain 

类中导入  #import <UIKit/UIKit.h> 不然调用 UIDevice 会报错

IDFVTools.h中

+ (void)save:(NSString *)service data:(id)data;

+ (id)load:(NSString *)service;

+ (void)delete:(NSString *)service;

+ (NSString *)getIDFV;
IDFVTools.m中
+ (NSString *)getIDFV

{
    //定义存入keychain中的账号 一个标识 表示是某个app存储的内容 bundle id最好
    NSString * const KEY_USERNAME = @"com.wdw.zzzz.username";
    NSString * const KEY_PASSWORD = @"com.wdw.zzzz.password";
    
    //测试用 清除keychain中的内容
    //[IDFVTools delete:KEY_USERNAME_PASSWORD];
    
    //读取账号中保存的内容
    NSMutableDictionary *readUserDataDic = (NSMutableDictionary *)[IDFVTools load:KEY_USERNAME];
    //NSLog(@"keychain==%@",readUserDataDic);
    
    
    if (!readUserDataDic)
    {//如果是第一次 肯定获取不到 这个时候就存储一个
        
        NSString *deviceIdStr = [[[UIDevice currentDevice] identifierForVendor] UUIDString];//获取IDFV
        //NSLog(@"identifierStr==%@",identifierStr);
        
        NSMutableDictionary *needSaveDataDic = [NSMutableDictionary dictionaryWithObject:deviceIdStr forKey:KEY_PASSWORD];
        //进行存储 并返回这个数据
        [IDFVTools save:KEY_USERNAME data:needSaveDataDic];
        
        return deviceIdStr;
    }
    else{return [readUserDataDic objectForKey:KEY_PASSWORD];}
}

//储存
+ (void)save:(NSString *)service data:(id)data
{
    //Get search dictionary
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    
    //Delete old item before add new item
    SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
    
    //Add new object to search dictionary(Attention:the data format)
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge id)kSecValueData];
    
    //Add item to keychain with the search dictionary
    SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
}

+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service
{
    return [NSMutableDictionary dictionaryWithObjectsAndKeys: (__bridge id)kSecClassGenericPassword,(__bridge id)kSecClass, service, (__bridge id)kSecAttrService, service, (__bridge id)kSecAttrAccount, (__bridge id)kSecAttrAccessibleAfterFirstUnlock,(__bridge id)kSecAttrAccessible, nil];
}

//取出
+ (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 attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue
    
    [keychainQuery setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
    
    [keychainQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
    
    CFDataRef keyData = NULL;
    
    if (SecItemCopyMatching((__bridge 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)delete:(NSString *)service
{
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
}

哪里需要调用只需要

[IDFVTools getIDFV];

彩蛋!

除了上述方式还有一个操作钥匙串的好用的第三方,就是SAMKeychain!

如果你的项目用了cocopods  那么可以直接下载 具体可以看这里:

点击打开链接

如果你的项目第三方框架都是手动导入 不喜欢用cocopods 可以直接参考gitHub:

点击打开链接

调用的时候我们可以封装成一个方法:

+ (NSString *)getUniqueDevice
{
    NSString *appName=[[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleNameKey];
    NSString *strApplicationUUID =  [SAMKeychain passwordForService:appName account:account];
    if (strApplicationUUID == nil)
    {
        strApplicationUUID  = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
        NSError *error = nil;
        SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init];
        query.service = appName;
        query.account = account;
        query.password = strApplicationUUID;
        query.synchronizationMode = SAMKeychainQuerySynchronizationModeNo;
        [query save:&error];

    }
    return strApplicationUUID;
}




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值