Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

better preserve the Device UUID across apps by using the keychain ins…

…tead of app local storage

use identifierForVendor to have multiple apps on a device share a UUID when possible
  • Loading branch information...
commit 256948aa13ee2ea6396ec5b309ec72706a758de9 1 parent 65c5785
@theganyo theganyo authored
View
357 UGAPI/SSKeychain.h
@@ -0,0 +1,357 @@
+//
+// SSKeychain.h
+// SSToolkit
+//
+// Created by Sam Soffes on 5/19/10.
+// Copyright (c) 2009-2011 Sam Soffes. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <Security/Security.h>
+
+/** Error codes that can be returned in NSError objects. */
+typedef enum {
+ /** No error. */
+ SSKeychainErrorNone = noErr,
+
+ /** Some of the arguments were invalid. */
+ SSKeychainErrorBadArguments = -1001,
+
+ /** There was no password. */
+ SSKeychainErrorNoPassword = -1002,
+
+ /** One or more parameters passed internally were not valid. */
+ SSKeychainErrorInvalidParameter = errSecParam,
+
+ /** Failed to allocate memory. */
+ SSKeychainErrorFailedToAllocated = errSecAllocate,
+
+ /** No trust results are available. */
+ SSKeychainErrorNotAvailable = errSecNotAvailable,
+
+ /** Authorization/Authentication failed. */
+ SSKeychainErrorAuthorizationFailed = errSecAuthFailed,
+
+ /** The item already exists. */
+ SSKeychainErrorDuplicatedItem = errSecDuplicateItem,
+
+ /** The item cannot be found.*/
+ SSKeychainErrorNotFound = errSecItemNotFound,
+
+ /** Interaction with the Security Server is not allowed. */
+ SSKeychainErrorInteractionNotAllowed = errSecInteractionNotAllowed,
+
+ /** Unable to decode the provided data. */
+ SSKeychainErrorFailedToDecode = errSecDecode
+} SSKeychainErrorCode;
+
+extern NSString *const kSSKeychainErrorDomain;
+
+/** Account name. */
+extern NSString *const kSSKeychainAccountKey;
+
+/**
+ Time the item was created.
+
+ The value will be a string.
+ */
+extern NSString *const kSSKeychainCreatedAtKey;
+
+/** Item class. */
+extern NSString *const kSSKeychainClassKey;
+
+/** Item description. */
+extern NSString *const kSSKeychainDescriptionKey;
+
+/** Item label. */
+extern NSString *const kSSKeychainLabelKey;
+
+/** Time the item was last modified.
+
+ The value will be a string.
+ */
+extern NSString *const kSSKeychainLastModifiedKey;
+
+/** Where the item was created. */
+extern NSString *const kSSKeychainWhereKey;
+
+/**
+ Simple wrapper for accessing accounts, getting passwords, setting passwords, and deleting passwords using the system
+ Keychain on Mac OS X and iOS.
+
+ This was originally inspired by EMKeychain and SDKeychain (both of which are now gone). Thanks to the authors.
+ SSKeychain has since switched to a simpler implementation that was abstracted from [SSToolkit](http://sstoolk.it).
+ */
+@interface SSKeychain : NSObject
+
+///-----------------------
+/// @name Getting Accounts
+///-----------------------
+
+/**
+ Returns an array containing the Keychain's accounts, or `nil` if the Keychain has no accounts.
+
+ See the `NSString` constants declared in SSKeychain.h for a list of keys that can be used when accessing the
+ dictionaries returned by this method.
+
+ @return An array of dictionaries containing the Keychain's accounts, or `nil` if the Keychain doesn't have any
+ accounts. The order of the objects in the array isn't defined.
+
+ @see allAccounts:
+ */
++ (NSArray *)allAccounts;
+
+/**
+ Returns an array containing the Keychain's accounts, or `nil` if the Keychain doesn't have any
+ accounts.
+
+ See the `NSString` constants declared in SSKeychain.h for a list of keys that can be used when accessing the
+ dictionaries returned by this method.
+
+ @param error If accessing the accounts fails, upon return contains an error that describes the problem.
+
+ @return An array of dictionaries containing the Keychain's accounts, or `nil` if the Keychain doesn't have any
+ accounts. The order of the objects in the array isn't defined.
+
+ @see allAccounts
+ */
++ (NSArray *)allAccounts:(NSError **)error;
+
+/**
+ Returns an array containing the Keychain's accounts for a given service, or `nil` if the Keychain doesn't have any
+ accounts for the given service.
+
+ See the `NSString` constants declared in SSKeychain.h for a list of keys that can be used when accessing the
+ dictionaries returned by this method.
+
+ @param serviceName The service for which to return the corresponding accounts.
+
+ @return An array of dictionaries containing the Keychain's accountsfor a given `serviceName`, or `nil` if the Keychain
+ doesn't have any accounts for the given `serviceName`. The order of the objects in the array isn't defined.
+
+ @see accountsForService:error:
+ */
++ (NSArray *)accountsForService:(NSString *)serviceName;
+
+/**
+ Returns an array containing the Keychain's accounts for a given service, or `nil` if the Keychain doesn't have any
+ accounts for the given service.
+
+ @param serviceName The service for which to return the corresponding accounts.
+
+ @param error If accessing the accounts fails, upon return contains an error that describes the problem.
+
+ @return An array of dictionaries containing the Keychain's accountsfor a given `serviceName`, or `nil` if the Keychain
+ doesn't have any accounts for the given `serviceName`. The order of the objects in the array isn't defined.
+
+ @see accountsForService:
+ */
++ (NSArray *)accountsForService:(NSString *)serviceName error:(NSError **)error;
+
+
+///------------------------
+/// @name Getting Passwords
+///------------------------
+
+/**
+ Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't have a
+ password for the given parameters.
+
+ @param serviceName The service for which to return the corresponding password.
+
+ @param account The account for which to return the corresponding password.
+
+ @return Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't
+ have a password for the given parameters.
+
+ @see passwordForService:account:error:
+ */
++ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
+
+/**
+ Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't have a
+ password for the given parameters.
+
+ @param serviceName The service for which to return the corresponding password.
+
+ @param account The account for which to return the corresponding password.
+
+ @param error If accessing the password fails, upon return contains an error that describes the problem.
+
+ @return Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't
+ have a password for the given parameters.
+
+ @see passwordForService:account:
+ */
++ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+
+/**
+ Returns the password data for a given account and service, or `nil` if the Keychain doesn't have data
+ for the given parameters.
+
+ @param serviceName The service for which to return the corresponding password.
+
+ @param account The account for which to return the corresponding password.
+
+ @param error If accessing the password fails, upon return contains an error that describes the problem.
+
+ @return Returns a the password data for the given account and service, or `nil` if the Keychain doesn't
+ have data for the given parameters.
+
+ @see passwordDataForService:account:error:
+ */
++ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account;
+
+/**
+ Returns the password data for a given account and service, or `nil` if the Keychain doesn't have data
+ for the given parameters.
+
+ @param serviceName The service for which to return the corresponding password.
+
+ @param account The account for which to return the corresponding password.
+
+ @param error If accessing the password fails, upon return contains an error that describes the problem.
+
+ @return Returns a the password data for the given account and service, or `nil` if the Keychain doesn't
+ have a password for the given parameters.
+
+ @see passwordDataForService:account:
+ */
++ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+
+
+///-------------------------
+/// @name Deleting Passwords
+///-------------------------
+
+/**
+ Deletes a password from the Keychain.
+
+ @param serviceName The service for which to delete the corresponding password.
+
+ @param account The account for which to delete the corresponding password.
+
+ @return Returns `YES` on success, or `NO` on failure.
+
+ @see deletePasswordForService:account:error:
+ */
++ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
+
+/**
+ Deletes a password from the Keychain.
+
+ @param serviceName The service for which to delete the corresponding password.
+
+ @param account The account for which to delete the corresponding password.
+
+ @param error If deleting the password fails, upon return contains an error that describes the problem.
+
+ @return Returns `YES` on success, or `NO` on failure.
+
+ @see deletePasswordForService:account:
+ */
++ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+
+
+///------------------------
+/// @name Setting Passwords
+///------------------------
+
+/**
+ Sets a password in the Keychain.
+
+ @param password The password to store in the Keychain.
+
+ @param serviceName The service for which to set the corresponding password.
+
+ @param account The account for which to set the corresponding password.
+
+ @return Returns `YES` on success, or `NO` on failure.
+
+ @see setPassword:forService:account:error:
+ */
++ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;
+
+/**
+ Sets a password in the Keychain.
+
+ @param password The password to store in the Keychain.
+
+ @param serviceName The service for which to set the corresponding password.
+
+ @param account The account for which to set the corresponding password.
+
+ @param error If setting the password fails, upon return contains an error that describes the problem.
+
+ @return Returns `YES` on success, or `NO` on failure.
+
+ @see setPassword:forService:account:
+ */
++ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+
+/**
+ Sets arbirary data in the Keychain.
+
+ @param password The data to store in the Keychain.
+
+ @param serviceName The service for which to set the corresponding password.
+
+ @param account The account for which to set the corresponding password.
+
+ @param error If setting the password fails, upon return contains an error that describes the problem.
+
+ @return Returns `YES` on success, or `NO` on failure.
+
+ @see setPasswordData:forService:account:error:
+ */
++ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account;
+
+/**
+ Sets arbirary data in the Keychain.
+
+ @param password The data to store in the Keychain.
+
+ @param serviceName The service for which to set the corresponding password.
+
+ @param account The account for which to set the corresponding password.
+
+ @param error If setting the password fails, upon return contains an error that describes the problem.
+
+ @return Returns `YES` on success, or `NO` on failure.
+
+ @see setPasswordData:forService:account:
+ */
++ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+
+
+///--------------------
+/// @name Configuration
+///--------------------
+
+#if __IPHONE_4_0 && TARGET_OS_IPHONE
+/**
+ Returns the accessibility type for all future passwords saved to the Keychain.
+
+ @return Returns the accessibility type.
+
+ The return value will be `NULL` or one of the "Keychain Item Accessibility Constants" used for determining when a
+ keychain item should be readable.
+
+ @see accessibilityType
+ */
++ (CFTypeRef)accessibilityType;
+
+/**
+ Sets the accessibility type for all future passwords saved to the Keychain.
+
+ @param accessibilityType One of the "Keychain Item Accessibility Constants" used for determining when a keychain item
+ should be readable.
+
+ If the value is `NULL` (the default), the Keychain default will be used.
+
+ @see accessibilityType
+ */
++ (void)setAccessibilityType:(CFTypeRef)accessibilityType;
+#endif
+
+@end
View
262 UGAPI/SSKeychain.m
@@ -0,0 +1,262 @@
+//
+// SSKeychain.m
+// SSToolkit
+//
+// Created by Sam Soffes on 5/19/10.
+// Copyright (c) 2009-2011 Sam Soffes. All rights reserved.
+//
+
+#import "SSKeychain.h"
+
+NSString *const kSSKeychainErrorDomain = @"com.samsoffes.sskeychain";
+
+NSString *const kSSKeychainAccountKey = @"acct";
+NSString *const kSSKeychainCreatedAtKey = @"cdat";
+NSString *const kSSKeychainClassKey = @"labl";
+NSString *const kSSKeychainDescriptionKey = @"desc";
+NSString *const kSSKeychainLabelKey = @"labl";
+NSString *const kSSKeychainLastModifiedKey = @"mdat";
+NSString *const kSSKeychainWhereKey = @"svce";
+
+#if __IPHONE_4_0 && TARGET_OS_IPHONE
+CFTypeRef SSKeychainAccessibilityType = NULL;
+#endif
+
+@interface SSKeychain ()
++ (NSMutableDictionary *)_queryForService:(NSString *)service account:(NSString *)account;
+@end
+
+@implementation SSKeychain
+
+#pragma mark - Getting Accounts
+
++ (NSArray *)allAccounts {
+ return [self accountsForService:nil error:nil];
+}
+
+
++ (NSArray *)allAccounts:(NSError **)error {
+ return [self accountsForService:nil error:error];
+}
+
+
++ (NSArray *)accountsForService:(NSString *)service {
+ return [self accountsForService:service error:nil];
+}
+
+
++ (NSArray *)accountsForService:(NSString *)service error:(NSError **)error {
+ OSStatus status = SSKeychainErrorBadArguments;
+ NSMutableDictionary *query = [self _queryForService:service account:nil];
+#if __has_feature(objc_arc)
+ [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];
+ [query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
+#else
+ [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
+ [query setObject:(id)kSecMatchLimitAll forKey:(id)kSecMatchLimit];
+#endif
+
+ CFTypeRef result = NULL;
+#if __has_feature(objc_arc)
+ status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
+#else
+ status = SecItemCopyMatching((CFDictionaryRef)query, &result);
+#endif
+ if (status != noErr && error != NULL) {
+ *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
+ return nil;
+ }
+
+#if __has_feature(objc_arc)
+ return (__bridge_transfer NSArray *)result;
+#else
+ return [(NSArray *)result autorelease];
+#endif
+}
+
+
+#pragma mark - Getting Passwords
+
++ (NSString *)passwordForService:(NSString *)service account:(NSString *)account {
+ return [self passwordForService:service account:account error:nil];
+}
+
+
++ (NSString *)passwordForService:(NSString *)service account:(NSString *)account error:(NSError **)error {
+ NSData *data = [self passwordDataForService:service account:account error:error];
+ if (data.length > 0) {
+ NSString *string = [[NSString alloc] initWithData:(NSData *)data encoding:NSUTF8StringEncoding];
+#if !__has_feature(objc_arc)
+ [string autorelease];
+#endif
+ return string;
+ }
+
+ return nil;
+}
+
+
++ (NSData *)passwordDataForService:(NSString *)service account:(NSString *)account {
+ return [self passwordDataForService:service account:account error:nil];
+}
+
+
++ (NSData *)passwordDataForService:(NSString *)service account:(NSString *)account error:(NSError **)error {
+ OSStatus status = SSKeychainErrorBadArguments;
+ if (!service || !account) {
+ if (error) {
+ *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
+ }
+ return nil;
+ }
+
+ CFTypeRef result = NULL;
+ NSMutableDictionary *query = [self _queryForService:service account:account];
+#if __has_feature(objc_arc)
+ [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
+ [query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
+ status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
+#else
+ [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
+ [query setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
+ status = SecItemCopyMatching((CFDictionaryRef)query, &result);
+#endif
+
+ if (status != noErr && error != NULL) {
+ *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
+ return nil;
+ }
+
+#if __has_feature(objc_arc)
+ return (__bridge_transfer NSData *)result;
+#else
+ return [(NSData *)result autorelease];
+#endif
+}
+
+
+#pragma mark - Deleting Passwords
+
++ (BOOL)deletePasswordForService:(NSString *)service account:(NSString *)account {
+ return [self deletePasswordForService:service account:account error:nil];
+}
+
+
++ (BOOL)deletePasswordForService:(NSString *)service account:(NSString *)account error:(NSError **)error {
+ OSStatus status = SSKeychainErrorBadArguments;
+ if (service && account) {
+ NSMutableDictionary *query = [self _queryForService:service account:account];
+#if __has_feature(objc_arc)
+ status = SecItemDelete((__bridge CFDictionaryRef)query);
+#else
+ status = SecItemDelete((CFDictionaryRef)query);
+#endif
+ }
+ if (status != noErr && error != NULL) {
+ *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
+ }
+ return (status == noErr);
+
+}
+
+
+#pragma mark - Setting Passwords
+
++ (BOOL)setPassword:(NSString *)password forService:(NSString *)service account:(NSString *)account {
+ return [self setPassword:password forService:service account:account error:nil];
+}
+
+
++ (BOOL)setPassword:(NSString *)password forService:(NSString *)service account:(NSString *)account error:(NSError **)error {
+ NSData *data = [password dataUsingEncoding:NSUTF8StringEncoding];
+ return [self setPasswordData:data forService:service account:account error:error];
+}
+
+
++ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)service account:(NSString *)account {
+ return [self setPasswordData:password forService:service account:account error:nil];
+}
+
+
++ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)service account:(NSString *)account error:(NSError **)error {
+ OSStatus status = SSKeychainErrorBadArguments;
+ if (password && service && account) {
+ [self deletePasswordForService:service account:account];
+ NSMutableDictionary *query = [self _queryForService:service account:account];
+#if __has_feature(objc_arc)
+ [query setObject:password forKey:(__bridge id)kSecValueData];
+#else
+ [query setObject:password forKey:(id)kSecValueData];
+#endif
+
+#if __IPHONE_4_0 && TARGET_OS_IPHONE
+ if (SSKeychainAccessibilityType) {
+#if __has_feature(objc_arc)
+ [query setObject:(id)[self accessibilityType] forKey:(__bridge id)kSecAttrAccessible];
+#else
+ [query setObject:(id)[self accessibilityType] forKey:(id)kSecAttrAccessible];
+#endif
+ }
+#endif
+
+#if __has_feature(objc_arc)
+ status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
+#else
+ status = SecItemAdd((CFDictionaryRef)query, NULL);
+#endif
+ }
+ if (status != noErr && error != NULL) {
+ *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
+ }
+ return (status == noErr);
+}
+
+
+#pragma mark - Configuration
+
+#if __IPHONE_4_0 && TARGET_OS_IPHONE
++ (CFTypeRef)accessibilityType {
+ return SSKeychainAccessibilityType;
+}
+
+
++ (void)setAccessibilityType:(CFTypeRef)accessibilityType {
+ CFRetain(accessibilityType);
+ if (SSKeychainAccessibilityType) {
+ CFRelease(SSKeychainAccessibilityType);
+ }
+ SSKeychainAccessibilityType = accessibilityType;
+}
+#endif
+
+
+#pragma mark - Private
+
++ (NSMutableDictionary *)_queryForService:(NSString *)service account:(NSString *)account {
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:3];
+#if __has_feature(objc_arc)
+ [dictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
+#else
+ [dictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
+#endif
+
+ if (service) {
+#if __has_feature(objc_arc)
+ [dictionary setObject:service forKey:(__bridge id)kSecAttrService];
+#else
+ [dictionary setObject:service forKey:(id)kSecAttrService];
+#endif
+ }
+
+ if (account) {
+#if __has_feature(objc_arc)
+ [dictionary setObject:account forKey:(__bridge id)kSecAttrAccount];
+#else
+ [dictionary setObject:account forKey:(id)kSecAttrAccount];
+#endif
+ }
+
+ return dictionary;
+}
+
+@end
View
7 UGAPI/UGClient.h
@@ -301,4 +301,11 @@ set the response limit in UGQuery as well.
// service communication in progress and help debug problems you may be having.
-(void)setLogging: (BOOL)loggingState;
+/*********************** VERSION CHECKING ************************/
+#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
+#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
+#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
+#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
+#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
+
@end
View
46 UGAPI/UGClient.m
@@ -2,6 +2,7 @@
#import "UGHTTPManager.h"
#import "SBJson.h"
#import "UGMultiStepAction.h"
+#import "SSKeychain.h"
NSString *g_deviceUUID = nil;
@@ -1142,35 +1143,38 @@ - (UGClientResponse *)pushAlert:(NSString *)message
/*************************** SERVER-SIDE STORAGE ***************************/
/*************************** SERVER-SIDE STORAGE ***************************/
/*************************** SERVER-SIDE STORAGE ***************************/
-// fun with uuids. Apple made this needlessly complex when they decided
-// developers were no longer allowed to access the device ID of the handset.
+(NSString *)getUniqueDeviceID
{
- // first, see if we have the value cached
- if ( g_deviceUUID ) return g_deviceUUID;
+ // cached?
+ if (g_deviceUUID) return g_deviceUUID;
- // next, see if we have the value in our database
+ // in our keychain?
+ g_deviceUUID = [SSKeychain passwordForService:@"Usergrid" account:@"DeviceUUID"];
+ if (g_deviceUUID) return g_deviceUUID;
+
+ // in the (legacy) app defaults?
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
- if ( !defaults ) return nil; // serious problems
g_deviceUUID = [defaults valueForKey:@"UGClientDeviceUUID"];
-
- // if we did, we're good
- if ( g_deviceUUID ) return g_deviceUUID;
- // if we're here, we need to create a unique ID
- CFUUIDRef uuidRef = CFUUIDCreate(nil);
- CFStringRef uuidStringRef = CFUUIDCreateString(nil, uuidRef);
- CFRelease(uuidRef);
+ // if none found in storage, generate one
+ if (!g_deviceUUID) {
- // convert it to a usable string. Make our own copy.
- g_deviceUUID = [NSString stringWithString:(__bridge NSString *)uuidStringRef];
- if ( !g_deviceUUID ) return nil;
-
- // store it
- [defaults setObject:g_deviceUUID forKey:@"UGClientDeviceUUID"];
- [defaults synchronize];
+ if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")) {
+ // use identifierForVendor where possible
+ g_deviceUUID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
+ }
+ else {
+ // otherwise, create a UUID (legacy method)
+ CFUUIDRef uuidRef = CFUUIDCreate(nil);
+ CFStringRef uuidStringRef = CFUUIDCreateString(nil, uuidRef);
+ CFRelease(uuidRef);
+ g_deviceUUID = [NSString stringWithString:(__bridge NSString *)uuidStringRef];
+ }
+ }
+
+ // store in keychain for future reference
+ [SSKeychain setPassword:g_deviceUUID forService:@"Usergrid" account:@"DeviceUUID"];
- // done
return g_deviceUUID;
}
View
18 UGAPIApp.xcodeproj/project.pbxproj
@@ -12,6 +12,8 @@
22D61E76171CBBF100E9CD17 /* UGHTTPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 22D61E70171CBBF100E9CD17 /* UGHTTPClient.m */; };
22D61E77171CBBF100E9CD17 /* UGHTTPHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 22D61E72171CBBF100E9CD17 /* UGHTTPHelpers.m */; };
22D61E78171CBBF100E9CD17 /* UGHTTPResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 22D61E74171CBBF100E9CD17 /* UGHTTPResult.m */; };
+ C96E782F1782349C004DD244 /* SSKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = C96E782E1782349C004DD244 /* SSKeychain.m */; };
+ C96E783717824711004DD244 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C96E783617824711004DD244 /* Security.framework */; };
E89D6CA1150E5B2E005F5FB9 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E89D6CA0150E5B2E005F5FB9 /* UIKit.framework */; };
E89D6CA3150E5B2E005F5FB9 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E89D6CA2150E5B2E005F5FB9 /* Foundation.framework */; };
E89D6CA5150E5B2E005F5FB9 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E89D6CA4150E5B2E005F5FB9 /* CoreGraphics.framework */; };
@@ -69,6 +71,9 @@
22D61E73171CBBF100E9CD17 /* UGHTTPResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UGHTTPResult.h; sourceTree = "<group>"; };
22D61E74171CBBF100E9CD17 /* UGHTTPResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UGHTTPResult.m; sourceTree = "<group>"; };
638F675816EF875F002BD513 /* UGClientDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UGClientDelegate.h; sourceTree = "<group>"; };
+ C96E782D1782349C004DD244 /* SSKeychain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSKeychain.h; sourceTree = "<group>"; };
+ C96E782E1782349C004DD244 /* SSKeychain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSKeychain.m; sourceTree = "<group>"; };
+ C96E783617824711004DD244 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
E89D6C9C150E5B2E005F5FB9 /* UGAPIApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UGAPIApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
E89D6CA0150E5B2E005F5FB9 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
E89D6CA2150E5B2E005F5FB9 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@@ -136,6 +141,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ C96E783717824711004DD244 /* Security.framework in Frameworks */,
E89D6D19150E5C54005F5FB9 /* CoreLocation.framework in Frameworks */,
E89D6CA1150E5B2E005F5FB9 /* UIKit.framework in Frameworks */,
E89D6CA3150E5B2E005F5FB9 /* Foundation.framework in Frameworks */,
@@ -171,9 +177,19 @@
path = v2;
sourceTree = "<group>";
};
+ C96E782917821C9D004DD244 /* SSKeychain */ = {
+ isa = PBXGroup;
+ children = (
+ C96E782D1782349C004DD244 /* SSKeychain.h */,
+ C96E782E1782349C004DD244 /* SSKeychain.m */,
+ );
+ name = SSKeychain;
+ sourceTree = "<group>";
+ };
E89D6C91150E5B2E005F5FB9 = {
isa = PBXGroup;
children = (
+ C96E783617824711004DD244 /* Security.framework */,
22CA67231732087600C03C70 /* Default-568h@2x.png */,
E89D6D18150E5C54005F5FB9 /* CoreLocation.framework */,
E89D6CDC150E5B99005F5FB9 /* UGAPI */,
@@ -251,6 +267,7 @@
E89D6CDC150E5B99005F5FB9 /* UGAPI */ = {
isa = PBXGroup;
children = (
+ C96E782917821C9D004DD244 /* SSKeychain */,
22D61E6C171CBBF100E9CD17 /* v2 */,
E89D6CDD150E5B99005F5FB9 /* SBJson */,
E89D6CF7150E5B99005F5FB9 /* UGActivity.h */,
@@ -439,6 +456,7 @@
22D61E76171CBBF100E9CD17 /* UGHTTPClient.m in Sources */,
22D61E77171CBBF100E9CD17 /* UGHTTPHelpers.m in Sources */,
22D61E78171CBBF100E9CD17 /* UGHTTPResult.m in Sources */,
+ C96E782F1782349C004DD244 /* SSKeychain.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

2 comments on commit 256948a

@pdardeau

Looks good to me. This is probably as good as we can get for support of iOS below 6.0.

@theganyo
Collaborator

Cool. Can you commit the PR? Thanks!

Please sign in to comment.
Something went wrong with that request. Please try again.