Created
August 22, 2011 01:16
-
-
Save akisute/1161447 to your computer and use it in GitHub Desktop.
Unique Installation Identifier (UIID) as a replacement of the depreciated UDID. Repository available here: https://github.com/akisute/UIApplication-UIID
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
// | |
// UIApplication+UIID.h | |
// UIID | |
// | |
// Created by akisute on 11/08/22. | |
// | |
#import <UIKit/UIKit.h> | |
/*! | |
Define UIID_PERSISTENT=1 to enable persistent UIID via Keychain. | |
This feature requires Security.framework to be included to your projects. | |
*/ | |
#ifndef UIID_PERSISTENT | |
#define UIID_PERSISTENT 0 | |
#endif | |
@interface UIApplication (UIApplication_UIID) | |
/*! | |
Returns the unique identifier for this installation of the application. | |
This value will change when: | |
- If UIID_PERSISTENT=1, -resetUniqueInstallationIdentifier is called or the application is installed to the completely new device without using any backups. | |
- If UIID_PERSISTENT=0, -resetUniqueInstallationIdentifier is called or the application is removed from the current device. | |
*/ | |
- (NSString *)uniqueInstallationIdentifier; | |
/*! Resets the persisted unique identifier for this installation. */ | |
- (void)resetUniqueInstallationIdentifier; | |
@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
// | |
// UIApplication+UIID.m | |
// UIID | |
// | |
// Created by akisute on 11/08/22. | |
// | |
#import "UIApplication+UIID.h" | |
#if UIID_PERSISTENT | |
// Use keychain as a storage | |
#import <Security/Security.h> | |
#endif | |
static NSString * const UIApplication_UIID_Key = @"uniqueInstallationIdentifier"; | |
@implementation UIApplication (UIApplication_UIID) | |
- (NSString *)uniqueInstallationIdentifier | |
{ | |
// Search for already created UIID | |
// If found, return it | |
// If not found, create a new UUID as a new UIID of this installation and save it | |
NSString *uuidString = nil; | |
#if UIID_PERSISTENT | |
// UIID must be persistent even if the application is removed from devices | |
// Use keychain as a storage | |
NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys: | |
(id)kSecClassGenericPassword, (id)kSecClass, | |
UIApplication_UIID_Key, (id)kSecAttrGeneric, | |
UIApplication_UIID_Key, (id)kSecAttrAccount, | |
[[NSBundle mainBundle] bundleIdentifier],(id)kSecAttrService, | |
(id)kSecMatchLimitOne, (id)kSecMatchLimit, | |
(id)kCFBooleanTrue, (id)kSecReturnAttributes, | |
nil]; | |
NSDictionary *attributes = nil; | |
OSStatus result = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&attributes); | |
if (result == noErr) { | |
NSMutableDictionary *valueQuery = [NSMutableDictionary dictionaryWithDictionary:attributes]; | |
[attributes release]; | |
[valueQuery setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; | |
[valueQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; | |
NSData *passwordData = nil; | |
OSStatus result = SecItemCopyMatching((CFDictionaryRef)valueQuery, (CFTypeRef *)&passwordData); | |
if (result == noErr) { | |
// Assume the stored data is a UTF-8 string. | |
uuidString = [[[NSString alloc] initWithBytes:[passwordData bytes] | |
length:[passwordData length] | |
encoding:NSUTF8StringEncoding] autorelease]; | |
[passwordData release]; | |
} | |
} | |
#else | |
// UIID may not be persistent | |
// Use NSUserDefalt as a storage | |
// WARNING: this could be much more vulnerable since the NSUserDefaults stores values as a plist file. Any jailbroken user can extract values from it. | |
uuidString = [[NSUserDefaults standardUserDefaults] stringForKey:UIApplication_UIID_Key]; | |
#endif | |
if (uuidString == nil) { | |
CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault); | |
uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuidRef); | |
[uuidString autorelease]; | |
CFRelease(uuidRef); | |
#if UIID_PERSISTENT | |
// UIID must be persistent even if the application is removed from devices | |
// Use keychain as a storage | |
NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys: | |
(id)kSecClassGenericPassword, (id)kSecClass, | |
UIApplication_UIID_Key, (id)kSecAttrGeneric, | |
UIApplication_UIID_Key, (id)kSecAttrAccount, | |
[[NSBundle mainBundle] bundleIdentifier], (id)kSecAttrService, | |
@"", (id)kSecAttrLabel, | |
@"", (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:(id)kSecAttrAccessibleAfterFirstUnlock forKey:(id)kSecAttrAccessible]; | |
} | |
[query setObject:[uuidString dataUsingEncoding:NSUTF8StringEncoding] forKey:(id)kSecValueData]; | |
OSStatus result = SecItemAdd((CFDictionaryRef)query, NULL); | |
if (result != noErr) { | |
NSLog(@"[ERROR] Couldn't add the Keychain Item. result = %ld query = %@", result, query); | |
return nil; | |
} | |
#else | |
// UIID may not be persistent | |
// Use NSUserDefalt as a storage | |
// WARNING: this could be much more vulnerable since the NSUserDefaults stores values as a plist file. Any jailbroken user can extract values from it. | |
[[NSUserDefaults standardUserDefaults] setObject:uuidString forKey:UIApplication_UIID_Key]; | |
#endif | |
} | |
return uuidString; | |
} | |
- (void)resetUniqueInstallationIdentifier | |
{ | |
#if UIID_PERSISTENT | |
// UIID must be persistent even if the application is removed from devices | |
// Use keychain as a storage | |
NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys: | |
(id)kSecClassGenericPassword, (id)kSecClass, | |
UIApplication_UIID_Key, (id)kSecAttrGeneric, | |
UIApplication_UIID_Key, (id)kSecAttrAccount, | |
[[NSBundle mainBundle] bundleIdentifier],(id)kSecAttrService, | |
nil]; | |
OSStatus result = SecItemDelete((CFDictionaryRef)query); | |
if (result == noErr) { | |
NSLog(@"[INFO} Unique Installation Identifier is successfully reset."); | |
} else if (result == errSecItemNotFound) { | |
NSLog(@"[INFO} Unique Installation Identifier is successfully reset."); | |
} else { | |
NSLog(@"[ERROR] Coudn't delete the Keychain Item. result = %ld query = %@", result, query); | |
} | |
#else | |
// UIID may not be persistent | |
// Use NSUserDefalt as a storage | |
// WARNING: this could be much more vulnerable since the NSUserDefaults stores values as a plist file. Any jailbroken user can extract values from it. | |
[[NSUserDefaults standardUserDefaults] removeObjectForKey:UIApplication_UIID_Key]; | |
NSLog(@"[INFO} Unique Installation Identifier is successfully reset."); | |
#endif | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment