如何在iPhone中获得唯一ID

时间:2016-05-25 04:55:48

标签: ios objective-c iphone

我的应用需要一个唯一ID。这个独特的id永远不会改变。当用户卸载应用程序或杀死应用程序时,不应更改它。我搜索了一些东西,但我不需要UUID。我决定使用以下代码获取设备令牌:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
    {
        [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
        [[UIApplication sharedApplication] registerForRemoteNotifications];

    }
    else
    {
        [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeNewsstandContentAvailability| UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];

    }

    return YES;
}
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings // NS_AVAILABLE_IOS(8_0);
{
    [application registerForRemoteNotifications];
}



- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken{


    NSString *token = [[deviceToken description] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    token = [token stringByReplacingOccurrencesOfString:@" " withString:@""];
    NSLog(@"content---%@", token);

}

有一个问题。在iOS 7中,我获得了设备令牌不变,卸载了应用程序并杀死了应用程序,并使设备令牌保持不变。但是在iOS 8及更高版本中,设备令牌不是常量。当用户杀死应用程序时,设备令牌会更改。我的应用程序需要一个唯一的设备令牌,或者获取唯一ID的任何其他方式。我该如何解决这个问题?帮我。谢谢你。

2 个答案:

答案 0 :(得分:2)

您必须将值存储在KeyChain中。 在AppDelegate.m文件中导入“KeychainItemWrapper.h”

中添加这些代码

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

//+++++++++++++++for unique identifier of the app++++++++++++++++
[[NSUserDefaults standardUserDefaults] setObject:@"" forKey:@"uniqueIdentifier"];

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyUniqueId" accessGroup:nil];

NSString *strIdent;

if ([[keychainItem objectForKey:(id)kSecAttrAccount] isEqualToString:@""]) {
    NSString *uniqueIdentifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    NSLog(@"uniqueIdentifier=%@",uniqueIdentifier);
    [keychainItem setObject:uniqueIdentifier forKey:(id)kSecAttrAccount];
    strIdent = [keychainItem objectForKey:(id)kSecAttrAccount];
    [[NSUserDefaults standardUserDefaults] setObject:strIdent forKey:@"uniqueIdentifier"];



}else{
    strIdent=[keychainItem objectForKey:(id)kSecAttrAccount];
    [[NSUserDefaults standardUserDefaults] setObject:strIdent forKey:@"uniqueIdentifier"];
}

NSLog(@"strIdent=%@",strIdent);
[[NSUserDefaults standardUserDefaults] synchronize];

//[keychainItem resetKeychainItem]; // to reset the keychain

// +++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++

KeychainItemWrapper.h

+++++++++++++++++++

#import <UIKit/UIKit.h>
@interface KeychainItemWrapper : NSObject

// Designated initializer.
- (id)initWithIdentifier: (NSString *)identifier accessGroup:(NSString *)accessGroup;
- (void)setObject:(id)inObject forKey:(id)key;
- (id)objectForKey:(id)key;

// Initializes and resets the default generic keychain item data.
- (void)resetKeychainItem;

@end

+++++++++++++++++++++++++++++++

KeychainItemWrapper.m

+++++++++++++++++++++++++++++++

#define PASSWORD_USES_DATA

#import "KeychainItemWrapper.h"
#import <Security/Security.h>



@interface KeychainItemWrapper (PrivateMethods)

- (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert;
- (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert;

- (void)writeToKeychain;

@end

@implementation KeychainItemWrapper
{
NSMutableDictionary *keychainItemData;      // The actual keychain item data backing store.
NSMutableDictionary *genericPasswordQuery;  // A placeholder for the generic keychain item query used to locate the item.
}

- (id)initWithIdentifier: (NSString *)identifier accessGroup:(NSString *) accessGroup;
{
if (self = [super init])
{

    genericPasswordQuery = [[NSMutableDictionary alloc] init];

    [genericPasswordQuery setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
    [genericPasswordQuery setObject:identifier forKey:(__bridge id)kSecAttrGeneric];



    if (accessGroup != nil)
    {
#if TARGET_IPHONE_SIMULATOR
        // Ignore the access group if running on the iPhone simulator.
        // 
        // Apps that are built for the simulator aren't signed, so there's no keychain access group
        // for the simulator to check. This means that all apps can see all keychain items when run
        // on the simulator.
        //
        // If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
        // simulator will return -25243 (errSecNoAccessForItem).
#else           
        [genericPasswordQuery setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
#endif
    }

    // Use the proper search constants, return only the attributes of the first match.
    [genericPasswordQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
    [genericPasswordQuery setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];

    NSDictionary *tempQuery = [NSDictionary dictionaryWithDictionary:genericPasswordQuery];

    CFMutableDictionaryRef outDictionary = NULL;

    if (!(SecItemCopyMatching((__bridge CFDictionaryRef)tempQuery, (CFTypeRef *)&outDictionary) == noErr))
    {
        // Stick these default values into keychain item if nothing found.
        [self resetKeychainItem];

        // Add the generic attribute and the keychain access group.
        [keychainItemData setObject:identifier forKey:(__bridge id)kSecAttrGeneric];
        if (accessGroup != nil)
        {
#if TARGET_IPHONE_SIMULATOR
            // Ignore the access group if running on the iPhone simulator.
            // 
            // Apps that are built for the simulator aren't signed, so there's no keychain access group
            // for the simulator to check. This means that all apps can see all keychain items when run
            // on the simulator.
            //
            // If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
            // simulator will return -25243 (errSecNoAccessForItem).
#else           
            [keychainItemData setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
#endif
        }
    }
    else
    {
        // load the saved data from Keychain.
        keychainItemData = [self secItemFormatToDictionary:(__bridge NSDictionary *)outDictionary];
    }
    if(outDictionary) CFRelease(outDictionary);
}

return self;
}

- (void)setObject:(id)inObject forKey:(id)key 
{
if (inObject == nil) return;
id currentObject = [keychainItemData objectForKey:key];
if (![currentObject isEqual:inObject])
{
    [keychainItemData setObject:inObject forKey:key];
    [self writeToKeychain];
}
}

- (id)objectForKey:(id)key
{
 return [keychainItemData objectForKey:key];
}

- (void)resetKeychainItem
{
if (!keychainItemData) 
{
    keychainItemData = [[NSMutableDictionary alloc] init];
}
else if (keychainItemData)
{
    NSMutableDictionary *tempDictionary = [self dictionaryToSecItemFormat:keychainItemData];
#ifndef NS_BLOCK_ASSERTIONS
    OSStatus junk = 
#endif
        SecItemDelete((__bridge CFDictionaryRef)tempDictionary);
    NSAssert( junk == noErr || junk == errSecItemNotFound, @"Problem deleting current dictionary." );
}

// Default attributes for keychain item.
[keychainItemData setObject:@"" forKey:(__bridge id)kSecAttrAccount];
[keychainItemData setObject:@"" forKey:(__bridge id)kSecAttrLabel];
[keychainItemData setObject:@"" forKey:(__bridge id)kSecAttrDescription];

// Default data for keychain item.
#ifndef PASSWORD_USES_DATA
[keychainItemData setObject:@"" forKey:(__bridge id)kSecValueData];
#else
[keychainItemData setObject:[NSData data] forKey:(__bridge id)kSecValueData];
#endif
}

- (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert
{
// The assumption is that this method will be called with a properly populated dictionary
// containing all the right key/value pairs for a SecItem.

// Create a dictionary to return populated with the attributes and data.
NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];

// Add the Generic Password keychain item class attribute.
[returnDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];

// Convert the NSString to NSData to meet the requirements for the value type kSecValueData.
// This is where to store sensitive data that should be encrypted.
#ifndef PASSWORD_USES_DATA
// orig
NSString *passwordString = [dictionaryToConvert objectForKey:(__bridge id)kSecValueData];
[returnDictionary setObject:[passwordString dataUsingEncoding:NSUTF8StringEncoding] forKey:(__bridge id)kSecValueData];
#else
// DFH
id val = [dictionaryToConvert objectForKey:(__bridge id)kSecValueData];
if([val isKindOfClass:[NSString class]]) {
    val = [(NSString *)val dataUsingEncoding:NSUTF8StringEncoding];
}
[returnDictionary setObject:val forKey:(__bridge id)kSecValueData];
#endif


return returnDictionary;
}

- (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert
{
// The assumption is that this method will be called with a properly populated dictionary
// containing all the right key/value pairs for the UI element.

// Create a dictionary to return populated with the attributes and data.
NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];

// Add the proper search key and class attribute.
[returnDictionary setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
[returnDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];

// Acquire the password data from the attributes.
CFDataRef passwordData = NULL;
if (SecItemCopyMatching((__bridge CFDictionaryRef)returnDictionary, (CFTypeRef *)&passwordData) == noErr)
{
    // Remove the search, class, and identifier key/value, we don't need them anymore.
    [returnDictionary removeObjectForKey:(__bridge id)kSecReturnData];

#ifndef PASSWORD_USES_DATA
    // Add the password to the dictionary, converting from NSData to NSString.
    NSString *password = [[NSString alloc] initWithBytes:[(__bridge NSData *)passwordData bytes] length:[(__bridge NSData *)passwordData length] 
                                                 encoding:NSUTF8StringEncoding];
#else
    NSData *password = (__bridge_transfer NSData *)passwordData;
    passwordData = NULL;
#endif
    [returnDictionary setObject:password forKey:(__bridge id)kSecValueData];
}
else
{
    // Don't do anything if nothing is found.
    NSAssert(NO, @"Serious error, no matching item found in the keychain.\n");
}
if(passwordData) CFRelease(passwordData);

return returnDictionary;
}

- (void)writeToKeychain
{
CFDictionaryRef attributes = NULL;
NSMutableDictionary *updateItem = nil;
OSStatus result;

if (SecItemCopyMatching((__bridge CFDictionaryRef)genericPasswordQuery, (CFTypeRef *)&attributes) == noErr)
{
    // First we need the attributes from the Keychain.
    updateItem = [NSMutableDictionary dictionaryWithDictionary:(__bridge NSDictionary *)attributes];
    // Second we need to add the appropriate search key/values.
    [updateItem setObject:[genericPasswordQuery objectForKey:(__bridge id)kSecClass] forKey:(__bridge id)kSecClass];

    // Lastly, we need to set up the updated attribute list being careful to remove the class.
    NSMutableDictionary *tempCheck = [self dictionaryToSecItemFormat:keychainItemData];
    [tempCheck removeObjectForKey:(__bridge id)kSecClass];

#if TARGET_IPHONE_SIMULATOR
    // Remove the access group if running on the iPhone simulator.
    // 
    // Apps that are built for the simulator aren't signed, so there's no keychain access group
    // for the simulator to check. This means that all apps can see all keychain items when run
    // on the simulator.
    //
    // If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
    // simulator will return -25243 (errSecNoAccessForItem).
    //
    // The access group attribute will be included in items returned by SecItemCopyMatching,
    // which is why we need to remove it before updating the item.
    [tempCheck removeObjectForKey:(__bridge id)kSecAttrAccessGroup];
#endif

    // An implicit assumption is that you can only update a single item at a time.
#ifndef NDEBUG      
    result = 
#endif
        SecItemUpdate((__bridge CFDictionaryRef)updateItem, (__bridge CFDictionaryRef)tempCheck);

    NSAssert( result == noErr, @"Couldn't update the Keychain Item." );
}
else
{
    // No previous item found; add the new one.
    result = SecItemAdd((__bridge CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL);
    NSAssert( result == noErr, @"Couldn't add the Keychain Item." );
}

if(attributes) CFRelease(attributes);
}

@end

答案 1 :(得分:1)

试试这个我希望它会有所帮助!!

NSString* Identifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; 
NSLog(@"output is : %@", Identifier);

在其他方面你可以做到这一点!!

NSString *udid = [[UIDevice currentDevice] uniqueIdentifier];

尝试使用其他选项!!

创建您自己的唯一ID并将其保存在钥匙串中。

使用供应商ID,如果同一供应商的所有应用都已从设备中移除,则会重置该供应商ID。