UIDevice uniqueIdentifier устарел - что теперь делать? - PullRequest
494 голосов
/ 09 августа 2011

Только что выяснилось, что свойство UIDevice uniqueIdentifier устарело в iOS 5 и недоступно в iOS 7 и выше. Похоже, что альтернативный метод или свойство недоступны или ожидаются.

Многие из наших существующих приложений тесно зависят от этого свойства для уникальной идентификации конкретного устройства. Как мы можем решить эту проблему в будущем?

Предложение от документации в 2011-2012 было:

Особые замечания

Не используйте свойство uniqueIdentifier. Для создания уникального идентификатора в своем приложении вы можете вызвать функцию CFUUIDCreate, чтобы создать UUID, и написать это в базу данных по умолчанию с использованием класса NSUserDefaults.

Однако это значение не будет таким, если пользователь удаляет и переустанавливает приложение.

Ответы [ 32 ]

1 голос
/ 04 июня 2015

Рабочий способ получить UDID:

  1. Запустите веб-сервер внутри приложения с двумя страницами: одна должна вернуть специально созданный профиль MobileConfiguration, а другая должна собрать UDID. Подробнее здесь , здесь и здесь .
  2. Вы открываете первую страницу в Mobile Safari из приложения, и она перенаправляет вас на Settings.app с просьбой установить профиль конфигурации. После установки профиля UDID отправляется на вторую веб-страницу, и вы можете получить к нему доступ из приложения. (Settings.app имеет все необходимые права и разные правила песочницы).

Пример использования RoutingHTTPServer :

import UIKit
import RoutingHTTPServer

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var bgTask = UIBackgroundTaskInvalid
    let server = HTTPServer()

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        application.openURL(NSURL(string: "http://localhost:55555")!)
        return true
    }

    func applicationDidEnterBackground(application: UIApplication) {
        bgTask = application.beginBackgroundTaskWithExpirationHandler() {
            dispatch_async(dispatch_get_main_queue()) {[unowned self] in
                application.endBackgroundTask(self.bgTask)
                self.bgTask = UIBackgroundTaskInvalid
            }
        }
    }
}

class HTTPServer: RoutingHTTPServer {
    override init() {
        super.init()
        setPort(55555)
        handleMethod("GET", withPath: "/") {
            $1.setHeader("Content-Type", value: "application/x-apple-aspen-config")
            $1.respondWithData(NSData(contentsOfFile: NSBundle.mainBundle().pathForResource("udid", ofType: "mobileconfig")!)!)
        }
        handleMethod("POST", withPath: "/") {
            let raw = NSString(data:$0.body(), encoding:NSISOLatin1StringEncoding) as! String
            let plistString = raw.substringWithRange(Range(start: raw.rangeOfString("<?xml")!.startIndex,end: raw.rangeOfString("</plist>")!.endIndex))
            let plist = NSPropertyListSerialization.propertyListWithData(plistString.dataUsingEncoding(NSISOLatin1StringEncoding)!, options: .allZeros, format: nil, error: nil) as! [String:String]

            let udid = plist["UDID"]! 
            println(udid) // Here is your UDID!

            $1.statusCode = 200
            $1.respondWithString("see https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/iPhoneOTAConfiguration/ConfigurationProfileExamples/ConfigurationProfileExamples.html")
        }
        start(nil)
    }
}

Вот содержимое udid.mobileconfig:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>PayloadContent</key>
        <dict>
            <key>URL</key>
            <string>http://localhost:55555</string>
            <key>DeviceAttributes</key>
            <array>
                <string>IMEI</string>
                <string>UDID</string>
                <string>PRODUCT</string>
                <string>VERSION</string>
                <string>SERIAL</string>
            </array>
        </dict>
        <key>PayloadOrganization</key>
        <string>udid</string>
        <key>PayloadDisplayName</key>
        <string>Get Your UDID</string>
        <key>PayloadVersion</key>
        <integer>1</integer>
        <key>PayloadUUID</key>
        <string>9CF421B3-9853-9999-BC8A-982CBD3C907C</string>
        <key>PayloadIdentifier</key>
        <string>udid</string>
        <key>PayloadDescription</key>
        <string>Install this temporary profile to find and display your current device's UDID. It is automatically removed from device right after you get your UDID.</string>
        <key>PayloadType</key>
        <string>Profile Service</string>
    </dict>
</plist>

Установка профиля не удастся (я не удосужился реализовать ожидаемый ответ, см. документацию ), но приложение получит правильный UDID. И вы также должны подписать mobileconfig .

1 голос
/ 25 февраля 2014

Вы можете использовать

NSString *sID = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];

, который уникален для устройства во всех приложениях.

0 голосов
/ 17 мая 2013

Маленький хак для тебя:

/**
 @method uniqueDeviceIdentifier
 @abstract A unique device identifier is a hash value composed from various hardware identifiers such
 as the device’s serial number. It is guaranteed to be unique for every device but cannot 
 be tied to a user account. [UIDevice Class Reference]
 @return An 1-way hashed identifier unique to this device.
 */
+ (NSString *)uniqueDeviceIdentifier {      
    NSString *systemId = nil;
    // We collect it as long as it is available along with a randomly generated ID.
    // This way, when this becomes unavailable we can map existing users so the
    // new vs returning counts do not break.
    if (([[[UIDevice currentDevice] systemVersion] floatValue] < 6.0f)) {
        SEL udidSelector = NSSelectorFromString(@"uniqueIdentifier");
        if ([[UIDevice currentDevice] respondsToSelector:udidSelector]) {
            systemId = [[UIDevice currentDevice] performSelector:udidSelector];
        }
    }
    else {
        systemId = [NSUUID UUID];
    }
    return systemId;
}
0 голосов
/ 13 мая 2013

Если кто-то наткнулся на этот вопрос, при поиске альтернативы. Я следовал этому подходу в IDManager классе, Это коллекция из разных решений. KeyChainUtil - это оболочка для чтения из цепочки для ключей. Вы также можете использовать hashed MAC address в качестве уникального идентификатора.

/*  Apple confirmed this bug in their system in response to a Technical Support Incident 
    request. They said that identifierForVendor and advertisingIdentifier sometimes 
    returning all zeros can be seen both in development builds and apps downloaded over the 
    air from the App Store. They have no work around and can't say when the problem will be fixed. */
#define kBuggyASIID             @"00000000-0000-0000-0000-000000000000"

+ (NSString *) getUniqueID {
    if (NSClassFromString(@"ASIdentifierManager")) {
        NSString * asiID = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
        if ([asiID compare:kBuggyASIID] == NSOrderedSame) {
            NSLog(@"Error: This device return buggy advertisingIdentifier.");
            return [IDManager getUniqueUUID];
        } else {
            return asiID;
        }

    } else {
        return [IDManager getUniqueUUID];
    }
}


+ (NSString *) getUniqueUUID {
    NSError * error;
    NSString * uuid = [KeychainUtils getPasswordForUsername:kBuyassUser andServiceName:kIdOgBetilngService error:&error];
    if (error) {
        NSLog(@"Error geting unique UUID for this device! %@", [error localizedDescription]);
        return nil;
    }
    if (!uuid) {
        DLog(@"No UUID found. Creating a new one.");
        uuid = [IDManager GetUUID];
        uuid = [Util md5String:uuid];
        [KeychainUtils storeUsername:USER_NAME andPassword:uuid forServiceName:SERVICE_NAME updateExisting:YES error:&error];
        if (error) {
            NSLog(@"Error getting unique UUID for this device! %@", [error localizedDescription]);
            return nil;
        }
    }
    return uuid;
}

/* NSUUID is after iOS 6. */
+ (NSString *)GetUUID
{
    CFUUIDRef theUUID = CFUUIDCreate(NULL);
    CFStringRef string = CFUUIDCreateString(NULL, theUUID);
    CFRelease(theUUID);
    return [(NSString *)string autorelease];
}

#pragma mark - MAC address
// Return the local MAC addy
// Courtesy of FreeBSD hackers email list
// Last fallback for unique identifier
+ (NSString *) getMACAddress
{
    int                 mib[6];
    size_t              len;
    char                *buf;
    unsigned char       *ptr;
    struct if_msghdr    *ifm;
    struct sockaddr_dl  *sdl;

    mib[0] = CTL_NET;
    mib[1] = AF_ROUTE;
    mib[2] = 0;
    mib[3] = AF_LINK;
    mib[4] = NET_RT_IFLIST;

    if ((mib[5] = if_nametoindex("en0")) == 0) {
        printf("Error: if_nametoindex error\n");
        return NULL;
    }

    if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
        printf("Error: sysctl, take 1\n");
        return NULL;
    }

    if ((buf = malloc(len)) == NULL) {
        printf("Error: Memory allocation error\n");
        return NULL;
    }

    if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
        printf("Error: sysctl, take 2\n");
        free(buf); // Thanks, Remy "Psy" Demerest
        return NULL;
    }

    ifm = (struct if_msghdr *)buf;
    sdl = (struct sockaddr_dl *)(ifm + 1);
    ptr = (unsigned char *)LLADDR(sdl);
    NSString *outstring = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];

    free(buf);
    return outstring;
}

+ (NSString *) getHashedMACAddress
{
    NSString * mac = [IDManager getMACAddress];
    return [Util md5String:mac];
}

+ (NSString *)md5String:(NSString *)plainText
{
    if(plainText == nil || [plainText length] == 0)
        return nil;

    const char *value = [plainText UTF8String];
    unsigned char outputBuffer[CC_MD5_DIGEST_LENGTH];
    CC_MD5(value, strlen(value), outputBuffer);

    NSMutableString *outputString = [[NSMutableString alloc] initWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
    for(NSInteger count = 0; count < CC_MD5_DIGEST_LENGTH; count++){
        [outputString appendFormat:@"%02x",outputBuffer[count]];
    }
    NSString * retString = [NSString stringWithString:outputString];
    [outputString release];
    return retString;
}
0 голосов
/ 30 декабря 2016

NSLog (@ "% @", [[UIDevice currentDevice] identifierForVendor]);

0 голосов
/ 17 сентября 2016

Для Swift 3.0 используйте код ниже.

let deviceIdentifier: String = (UIDevice.current.identifierForVendor?.uuidString)!
NSLog("output is : %@", deviceIdentifier)
0 голосов
/ 01 декабря 2014

Не идеальная, но одна из лучших и ближайших альтернатив UDID (в Swift с использованием iOS 8.1 и Xcode 6.1):

Генерация случайного UUID

let strUUID: String = NSUUID().UUIDString

И используйте KeychainWrapper Библиотека:

Добавить строковое значение в цепочку для ключей:

let saveSuccessful: Bool = KeychainWrapper.setString("Some String", forKey: "myKey")

Получить строковое значение из цепочки для ключей:

let retrievedString: String? = KeychainWrapper.stringForKey("myKey")

Удалить строковое значение из цепочки для ключей:

let removeSuccessful: Bool = KeychainWrapper.removeObjectForKey("myKey")

В этом решении используется цепочка для ключей, поэтому запись, хранящаяся в цепочке для ключей, будет сохраняться даже после удаления и переустановки приложения. Единственный способ удалить эту запись - сбросить все содержимое и настройки устройства. Вот почему я упомянул, что это решение замещения не является идеальным, но остается одним из лучших решений для замены UDID на iOS 8.1 с использованием Swift.

0 голосов
/ 18 апреля 2014

У меня тоже есть проблема, и решение простое:

    // Get Bundle Info for Remote Registration (handy if you have more than one app)
    NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
    NSString *appVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];


    // Get the users Device Model, Display Name, Unique ID, Token & Version Number
    UIDevice *dev = [UIDevice currentDevice];
    NSString *deviceUuid=[dev.identifierForVendor  UUIDString];

    NSString *deviceName = dev.name;
0 голосов
/ 23 января 2014

Apple скрыла UDID от всех общедоступных API, начиная с iOS 7. Любой UDID, начинающийся с FFFF, является поддельным идентификатором.Приложения «Отправить UDID», которые ранее работали, больше нельзя использовать для сбора UDID для тестовых устройств.(вздох!)

UDID отображается, когда устройство подключено к XCode (в органайзере) и когда устройство подключено к iTunes (хотя для получения идентификатора необходимо нажать «Серийный номер»).

Если вам нужно получить UDID для устройства, чтобы добавить его в профиль обеспечения, и вы не можете сделать это самостоятельно в XCode, вам придется пройти их шаги, чтобы скопировать / вставить егоиз iTunes.

Существует ли способ (выпуск iOS 7) получить UDID без использования iTunes на ПК / Mac?

0 голосов
/ 03 декабря 2013

Мы можем использовать идентификаторForVendor для ios7,

-(NSString*)uniqueIDForDevice
{
    NSString* uniqueIdentifier = nil;
    if( [UIDevice instancesRespondToSelector:@selector(identifierForVendor)] ) { // >=iOS 7
        uniqueIdentifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    } else { //<=iOS6, Use UDID of Device       
            CFUUIDRef uuid = CFUUIDCreate(NULL);
            //uniqueIdentifier = ( NSString*)CFUUIDCreateString(NULL, uuid);- for non- ARC
            uniqueIdentifier = ( NSString*)CFBridgingRelease(CFUUIDCreateString(NULL, uuid));// for ARC
            CFRelease(uuid);
         }
    }
return uniqueIdentifier;
}

- Важное примечание ---

UDID и идентификатор ForVendor различаются: ---

1.) On uninstalling  and reinstalling the app identifierForVendor will change.

2.) The value of identifierForVendor remains the same for all the apps installed from the same vendor on the device.

3.) The value of identifierForVendor also changes for all the apps if any of the app (from same vendor) is reinstalled.
...