Last active
December 29, 2015 13:18
-
-
Save kawanet/7676061 to your computer and use it in GitHub Desktop.
This category provides UUIDOfUser static method which saves and fetches an user identifier at user defaults or at key chain.
The code is based on @ux_design_tokyo's post: http://qiita.com/ux_design_tokyo/items/ace0caf4b909c785836f
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// NSUUID+UUIDOfUser.h | |
// | |
// Created by Yusuke Kawasaki on 2013/11/27. | |
// Copyright (c) 2013 Kawanet. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
@interface NSUUID (UUIDOfUser) | |
+ (NSUUID*)UUIDOfUser; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// NSUUID+UUIDOfUser.m | |
// | |
// Created by Yusuke Kawasaki on 2013/11/27. | |
// Based on @ux_design_tokyo's post: | |
// http://qiita.com/ux_design_tokyo/items/ace0caf4b909c785836f | |
// | |
#import "NSUUID+UUIDOfUser.h" | |
@implementation NSUUID (UUIDOfUser) | |
static NSString * const NSUUID_UUIDOfUser = @"NSUUID_UUIDOfUser"; | |
+ (NSUUID*)UUIDOfUser { | |
NSString *uuidString = nil; | |
// Check UserDefault at first | |
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; | |
uuidString = [userDefaults stringForKey:NSUUID_UUIDOfUser]; | |
BOOL notFoundAtSaveDefaults = !uuidString.length; | |
// Check keychain otherwise | |
if (!uuidString.length) { | |
NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys: | |
(__bridge id)kSecClassGenericPassword, (__bridge id)kSecClass, | |
NSUUID_UUIDOfUser, (__bridge id)kSecAttrGeneric, | |
NSUUID_UUIDOfUser, (__bridge id)kSecAttrAccount, | |
[[NSBundle mainBundle] bundleIdentifier],(__bridge id)kSecAttrService, | |
(__bridge id)kSecMatchLimitOne, (__bridge id)kSecMatchLimit, | |
(__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnAttributes, | |
nil]; | |
CFTypeRef attributesRef = NULL; | |
OSStatus result = SecItemCopyMatching((__bridge CFDictionaryRef)query, &attributesRef); | |
if (result == noErr) { | |
NSDictionary *attributes = (__bridge_transfer NSDictionary *)attributesRef; | |
NSMutableDictionary *valueQuery = [NSMutableDictionary dictionaryWithDictionary:attributes]; | |
[valueQuery setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; | |
[valueQuery setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; | |
CFTypeRef passwordDataRef = NULL; | |
OSStatus result = SecItemCopyMatching((__bridge CFDictionaryRef)valueQuery, &passwordDataRef); | |
if (result == noErr) { | |
NSData *passwordData = (__bridge_transfer NSData *)passwordDataRef; | |
// Assume the stored data is a UTF-8 string. | |
uuidString = [[NSString alloc] initWithBytes:[passwordData bytes] | |
length:[passwordData length] | |
encoding:NSUTF8StringEncoding]; | |
} | |
} | |
} | |
if (!uuidString.length) { | |
// Generate the new UIID | |
CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault); | |
uuidString = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuidRef); | |
CFRelease(uuidRef); | |
// iOS 6 has an invalid UUID bug: 00000000-0000-0000-0000-000000000000 | |
// id identifier = [[UIDevice currentDevice] performSelector:@selector(identifierForVendor)]; | |
// uuidString = [identifier performSelector:@selector(UUIDString)]; | |
// UIID must be persistent even if the application is removed from devices | |
// Use keychain as a storage | |
NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys: | |
(__bridge id)kSecClassGenericPassword, (__bridge id)kSecClass, | |
NSUUID_UUIDOfUser, (__bridge id)kSecAttrGeneric, | |
NSUUID_UUIDOfUser, (__bridge id)kSecAttrAccount, | |
[[NSBundle mainBundle] bundleIdentifier], (__bridge id)kSecAttrService, | |
@"", (__bridge id)kSecAttrLabel, | |
@"", (__bridge id)kSecAttrDescription, | |
nil]; | |
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 4) { | |
// kSecAttrAccessible is iOS 4 or later only | |
// Current device is running on iOS 3.X, do nothing here | |
} else { | |
// Set kSecAttrAccessibleAfterFirstUnlock so that background applications are able to access this key. | |
// Keys defined as kSecAttrAccessibleAfterFirstUnlock will be migrated to the new devices/installations via encrypted backups. | |
// If you want different UIID per device, use kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly instead. | |
// Keep in mind that keys defined as kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly will be removed after restoring from a backup. | |
[query setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(__bridge id)kSecAttrAccessible]; | |
} | |
[query setObject:[uuidString dataUsingEncoding:NSUTF8StringEncoding] forKey:(__bridge id)kSecValueData]; | |
OSStatus result = SecItemAdd((__bridge CFDictionaryRef)query, NULL); | |
if (result != noErr) { | |
NSLog(@"[ERROR] Couldn't add the Keychain Item. result = %ld query = %@", result, query); | |
return nil; | |
} | |
} | |
// save UUID at UserDefault | |
if (notFoundAtSaveDefaults) { | |
[userDefaults setObject:uuidString forKey:NSUUID_UUIDOfUser]; | |
} | |
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString]; | |
return uuid; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment