Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
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.