我的应用需要一个唯一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的任何其他方式。我该如何解决这个问题?帮我。谢谢你。
答案 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。