Przestarzałe UIDevice uniqueIdentifier - co teraz robić?


501

Właśnie wyszło na jaw, że właściwość unikalnego identyfikatora UIDevice jest przestarzała w iOS 5 i niedostępna w iOS 7 i nowszych. Wydaje się, że żadna alternatywna metoda lub właściwość nie jest dostępna ani nie będzie dostępna.

Wiele naszych istniejących aplikacji jest ściśle zależnych od tej właściwości w celu jednoznacznego identyfikowania konkretnego urządzenia. Jak poradzimy sobie z tym problemem w przyszłości?

Sugestia z dokumentacji w latach 2011-2012 była następująca:

Uwagi specjalne

Nie używaj właściwości uniqueIdentifier. Aby utworzyć unikalny identyfikator specyficzny dla aplikacji, możesz wywołać CFUUIDCreatefunkcję w celu utworzenia UUIDi zapisać ją w domyślnej bazie danych za pomocą NSUserDefaultsklasy.

Jednak ta wartość nie będzie taka sama, jeśli użytkownik odinstaluje i ponownie zainstaluje aplikację.


1
W przypadku aplikacji, które nadal używają unikalnego identyfikatora, iOS7 zwraca teraz FFFFFFFF + identyfikator ForVendor, który łamie wiele źle napisanych nieodnawialnych aplikacji subskrypcyjnych.
Rhythmic Fistman

Jeśli na szczęście Twoja aplikacja korzysta z powiadomień push, możesz użyć tokena odesłanego z usługi push Apple, jest również unikalny dla każdego urządzenia
Calin Chitu

@CalinChitu Jeśli użytkownik nie przyjmuje powiadomień push, czy nadal otrzymujesz dla niego identyfikator pushID?
Chase Roberts

Odpowiedzi:


272

UUID utworzony przez CFUUIDCreate jest unikalny, jeśli użytkownik odinstaluje i ponownie zainstaluje aplikację: za każdym razem otrzymasz nowy.

Ale możesz chcieć, aby nie był unikalny, tzn. Powinien pozostać niezmieniony, gdy użytkownik odinstaluje i ponownie zainstaluje aplikację. Wymaga to trochę wysiłku, ponieważ najbardziej wiarygodnym identyfikatorem na urządzenie wydaje się być adres MAC. Możesz zapytać o MAC i użyć go jako UUID.

Edycja: Oczywiście należy zawsze zapytać o MAC tego samego interfejsu. Chyba najlepszy jest zakład en0. MAC jest zawsze obecny, nawet jeśli interfejs nie ma adresu IP / jest wyłączony.

Edycja 2: Jak zauważyli inni, preferowanym rozwiązaniem od iOS 6 jest - [identyfikator UIDeviceForVendor] . W większości przypadków powinieneś być w stanie użyć go jako zamiennika starego -[UIDevice uniqueIdentifier](ale identyfikator UUID, który jest tworzony podczas pierwszego uruchomienia aplikacji, wydaje się, że Apple chce, abyś używał).

Edycja 3: Aby ten główny punkt nie zagubił się w szumie komentarza: nie używaj MAC jako UUID, utwórz skrót używając MAC . Ten skrót zawsze da ten sam wynik za każdym razem, nawet w przypadku ponownej instalacji i aplikacji (jeśli skrót jest wykonywany w ten sam sposób). W każdym razie, obecnie (2013) nie jest to już konieczne, chyba że potrzebujesz „stabilnego” identyfikatora urządzenia w iOS <6.0.

Edycja 4: W iOS 7 Apple zawsze zwraca teraz stałą wartość podczas wysyłania zapytania do MAC, aby udaremnić MAC jako podstawę dla schematu ID . Dlatego naprawdę powinieneś teraz użyć - [UIDevice identifierForVendor] lub utworzyć identyfikator UUID dla instalacji.


8
Czy adres mac nie zmienia się w zależności od tego, czy użytkownik jest podłączony przez Wi-Fi, czy nie?
Oliver Pearmain,

1
@DarkDust: ale ponieważ aktywny interfejs zmienia się po przełączeniu z Wi-Fi na modem komórkowy, adres MAC aktywnego interfejsu również powinien się zmienić; chyba że zawsze wybierzesz konkretny interfejs, aby uzyskać MAC
102008

3
@Roger Nolan: Nie edytuj odpowiedzi innych osób i nie dodawaj treści, które wyglądają, jakby pochodziły od oryginalnego autora. Dzięki.
DarkDust

2
@RogerNolan Dopóki post nie jest odpowiedzią społeczności, zmiany służą do korygowania błędów i takich rzeczy, a nie do dodawania nowych rzeczy. Jest powód, dla którego zdobyłeś ten przywilej. Wyobraź sobie, że zmieniam twoją odpowiedź i piszę BS, ludzie pomyśleliby, że to napisałeś. Wątpię, czy ci się to spodobało :-) Ale nie otrzymujesz powiadomienia, że nastąpiła zmiana, dowiedziałem się tylko przypadkiem.
DarkDust

3
Apple odrzuca teraz aplikacje korzystające z mieszanego adresu MAC.
Idan

91

Możesz już użyć swojej alternatywy dla Apple UDID. Gekitz, miły człowiek, napisał kategorię, na podstawie UIDevicektórej wygeneruje coś w UDIDoparciu o adres MAC urządzenia i identyfikator pakietu.

Możesz znaleźć kod na github


3
Ta implementacja jest unikalna (adres MAC) dla urządzenia w trakcie ponownej instalacji, na przykład unikatowy identyfikator Apple, ale także szanuje prywatność, jest również unikalna dla aplikacji (także używa bundleId) ... Musi mieć imho, które Apple powinien uwzględnić w swoich interfejsach API. .. zamiast przestarzałych bez żadnych alternatyw.
Vincent Guerci

8
Chociaż używa starej licencji bsd z klauzulą ​​reklamową, fuj.
jbtule

8
Dla każdego, kto teraz znajduje ten post, licencja została zmieniona od powyższego komentarza autorstwa jbtule.
James

1
Jak omówiono w tym komentarzu do zatwierdzenia, biblioteka w chwili obecnej stanowi poważny problem z wyciekiem prywatności i nie powinna być używana:
Czy

15
Począwszy od iOS 7, system zawsze zwraca wartość, 02:00:00:00:00:00gdy poprosisz o adres MAC na dowolnym urządzeniu. Sprawdź tutaj: developer.apple.com/library/prerelease/ios/releasenotes/General/…
Hejazi

61

Na podstawie linku zaproponowanego przez @moonlight wykonałem kilka testów i wydaje się to najlepszym rozwiązaniem. Jak mówi @DarkDust, metoda sprawdza, en0która jest zawsze dostępna.
Istnieją 2 opcje:
uniqueDeviceIdentifier(MD5 MAC + CFBundleIdentifier)
i uniqueGlobalDeviceIdentifier(MD5 MAC), zawsze zwracają te same wartości.
Poniżej testy, które przeprowadziłem (z prawdziwym urządzeniem):

#import "UIDevice+IdentifierAddition.h"

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

XXXX21f1f19edff198e2a2356bf4XXXX - (WIFI) UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (WIFI) GlobalAppUDID

XXXX21f1f19edff198e2a2356bf4XXXX - (3G) UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (3G) GlobalAppUDID

XXXX21f1f19edff198e2a2356bf4XXXX - (GPRS) UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (GPRS) GlobalAppUDID

XXXX21f1f19edff198e2a2356bf4XXXX - (tryb AirPlane) UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (tryb AirPlane) GlobalAppUDID

XXXX21f1f19edff198e2a2356bf4XXXX - (Wi-Fi) po usunięciu i ponownej instalacji aplikacji XXXX7dc3c577446a2bcbd77935bdXXXX (Wi-Fi) po usunięciu i zainstalowaniu aplikacji

Mam nadzieję, że to się przyda.

EDYCJA:
Jak zauważyli inni, to rozwiązanie w iOS 7 nie jest już przydatne, ponieważ uniqueIdentifiernie jest już dostępne, a zapytania o adres MAC zwracają teraz zawsze 02: 00: 00: 00: 00: 00


13
to nie będzie działać na iOS7, Apple eliminuje użycie adresu MAC.
Sarim Sidd,

@SarimSidd Na razie informacje o iOS 7 są objęte umową NDA, nie możemy tutaj omawiać.
Mat

57

Spójrz na to,

możemy użyć pęku kluczy zamiast NSUserDefaultsklasy, do przechowywania UUIDutworzonego przez CFUUIDCreate.

w ten sposób możemy uniknąć UUIDrekreacji z ponowną instalacją i uzyskać zawsze to samo UUIDdla tej samej aplikacji, nawet odinstalowanie użytkownika i ponowna instalacja.

UUID zostaną odtworzone właśnie po zresetowaniu urządzenia przez użytkownika.

Wypróbowałem tę metodę z SFHFKeychainUtils i działa jak urok.


33
Ta metoda jest solidnym zamiennikiem UDID. Ma także tę dodatkową zaletę, że odtwarza identyfikator po formacie urządzenia (np. Jeśli urządzenie zmieni właściciela). Należy jednak pamiętać, że pęku kluczy można przywrócić na innych urządzeniach, jeśli użytkownik zaszyfruje ich kopię zapasową. Może to spowodować sytuację, w której wiele urządzeń ma ten sam UUID. Aby tego uniknąć, ustaw dostępność elementu pęku kluczy na kSecAttrAccessibleAlwaysThisDeviceOnly. Zapewni to, że Twój UUID nie migruje na żadne inne urządzenie. Aby uzyskać dostęp do UUID z innych aplikacji, użyj kSecAttrAccessGroupklucza.
Jeevan Takhar

Gdzie dokładnie (którego klucza) należy użyć do przechowywania identyfikatora UUID w pęku kluczy?
lostintranslation

Opps! link jest zepsuty
COVID19

48

Utwórz własny identyfikator UUID, a następnie zapisz go w pęku kluczy. W ten sposób utrzymuje się nawet po odinstalowaniu aplikacji. W wielu przypadkach utrzymuje się również, nawet jeśli użytkownik migruje między urządzeniami (np. Pełna kopia zapasowa i przywracanie na inne urządzenie).

W efekcie staje się unikalnym identyfikatorem użytkownika . (nawet lepszy niż identyfikator urządzenia ).

Przykład:

Definiuję niestandardową metodę tworzenia UUIDjako:

- (NSString *)createNewUUID 
{
    CFUUIDRef theUUID = CFUUIDCreate(NULL);
    CFStringRef string = CFUUIDCreateString(NULL, theUUID);
    CFRelease(theUUID);
    return [(NSString *)string autorelease];
}

Następnie możesz zapisać go KEYCHAINprzy pierwszym uruchomieniu aplikacji. Dzięki temu po pierwszym uruchomieniu możemy po prostu użyć go z pęku kluczy, bez potrzeby jego ponownego generowania. Głównym powodem korzystania z pęku kluczy do przechowywania jest: Po ustawieniu UUIDpęku kluczy będzie on trwał, nawet jeśli użytkownik całkowicie odinstaluje aplikację, a następnie zainstaluje ją ponownie. . Jest to więc stały sposób przechowywania, co oznacza, że ​​klucz będzie wyjątkowy przez cały czas.

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

Podczas uruchamiania aplikacji dołącz następujący kod:

 // getting the unique key (if present ) from keychain , assuming "your app identifier" as a key
       NSString *retrieveuuid = [SSKeychain passwordForService:@"your app identifier" account:@"user"];
      if (retrieveuuid == nil) { // if this is the first time app lunching , create key for device
        NSString *uuid  = [self createNewUUID];
// save newly created key to Keychain
        [SSKeychain setPassword:uuid forService:@"your app identifier" account:@"user"];
// this is the one time process
}

Pobierz plik SSKeychain.m i .h z sskeychain i przeciągnij plik SSKeychain.m i .h do swojego projektu i dodaj „Security.framework” do swojego projektu. Aby później użyć UUID, po prostu użyj:

NSString *retrieveuuid = [SSKeychain passwordForService:@"your app identifier" account:@"user"];

Ponieważ identyfikatorForVendor działa nie jest idealnie. W niektórych przypadkach może zwrócić zero lub 0x0. Wydaje się, że ta metoda działa idealnie
CReaTuS,

3
czy ktoś potwierdził, że działa na iOS7 po cyklu odinstalowywania / ponownej instalacji + zweryfikowanym przesłaniu aplikacji Apple?
Mindbomb

Zacząłem używać tego rozwiązania. Kilka testów na 2 urządzeniach (przebuduj, zainstaluj ponownie, zamknij urządzenie) pokazało mi, że identyfikator jest taki sam. iOS 10.3.
deko

16

Być może możesz użyć:

[UIDevice currentDevice].identifierForVendor.UUIDString

Dokumentacja Apple opisuje identyfikator FVVender w następujący sposób:

Wartość tej właściwości jest taka sama dla aplikacji pochodzących od tego samego dostawcy działającego na tym samym urządzeniu. Inna wartość jest zwracana dla aplikacji na tym samym urządzeniu pochodzących od różnych dostawców oraz dla aplikacji na różnych urządzeniach, niezależnie od dostawcy.


Ciekawe, dlaczego nikt do tej pory o tym nie
wspominał

1
Jeśli użytkownik zaktualizuje iOS i / lub zainstaluje nowego iOS, to wartość identyfikatora ForVendor zmieni się lub pozostanie taka sama?
Sunil Zalavadiya

1
Po usunięciu wszystkich aplikacji od tego samego dostawcy wartość ta zostanie zmieniona.
Mitesh Khatri

14

Możesz zastanowić się nad użyciem tego, OpenUDIDktóry zastępuje przestarzałe UDID.

Zasadniczo, aby dopasować UDID, wymagane są następujące funkcje:

  1. unikalny lub wystarczająco unikalny (kolizja o niskim prawdopodobieństwie jest prawdopodobnie bardzo akceptowalna)
  2. wytrwałość podczas restartów, przywraca, odinstalowuje
  3. dostępne w aplikacjach różnych dostawców (przydatne do pozyskiwania użytkowników za pośrednictwem sieci CPI) -

OpenUDID spełnia powyższe wymagania, a nawet ma wbudowany mechanizm rezygnacji do późniejszego rozpatrzenia.

Sprawdź http://OpenUDID.org, który wskazuje odpowiedni GitHub. Mam nadzieję że to pomoże!

Na marginesie, unikałbym wszelkich alternatywnych adresów MAC. Chociaż adres MAC wydaje się kuszącym i uniwersalnym rozwiązaniem, upewnij się, że ten nisko wiszący owoc jest zatruty. Adres MAC jest bardzo wrażliwy i Apple może bardzo przestać mieć do niego dostęp, zanim jeszcze powiesz „ZGŁOŚ TĄ APLIKACJĘ” ... adres sieciowy MAC służy do uwierzytelniania niektórych urządzeń na prywatnych liniach (WLAN) lub innych wirtualnych prywatnych sieci (VPN). .. jest jeszcze bardziej czuły niż poprzedni UDID!


Jestem naprawdę ciekawy, jak to działa? Kod jest napisany w Objective-C, ale nie ma innego dobrego rozwiązania, które spełniałoby powyższe wymagania, więc co wyróżnia ten framework? Rozwiązanie, z którego korzysta ten framework, powinno być również możliwe do opublikowania jako sugerowana odpowiedź tutaj ...
jake_hetfield

Zgadzam się - adres MAC można również skonfigurować ręcznie („sklonowany”), choć w większości przypadków jest to mało prawdopodobne. Muszę zaprotestować przeciwko D w UDID. To nie jest identyfikator urządzenia, to UUID (uniwersalnie unikalny identyfikator). Identyfikator urządzenia jest stemplowany przez Apple z fabryki na każdym urządzeniu w pamięci ROM.
Jay Imerman

Najlepsze rozwiązanie dla iOS7 pozwala również na to, co naprawdę jest potrzebne do jednoznacznej identyfikacji urządzenia
vishal dharankar

1
OpenUDID jest przestarzały i nie jest zalecany do użycia
mkll

11

Jestem pewien, że Apple zirytowało wiele osób tą zmianą. I opracować aplikację księgowości dla iOS i posiada usługa online do synchronizacji zmian dokonanych na różnych urządzeniach. Usługa utrzymuje bazę danych wszystkich urządzeń i zmiany, które należy im przekazać. Dlatego ważne jest, aby wiedzieć, które urządzenia są które. Śledzę urządzenia korzystające z unikalnego identyfikatora UIDevice i za ile są warte, oto moje przemyślenia.

  • Wygenerować identyfikator UUID i zapisać w ustawieniach domyślnych użytkownika? Nic dobrego, ponieważ nie utrzymuje się, gdy użytkownik usuwa aplikację. Jeśli zainstalują się ponownie później, usługa online nie powinna utworzyć nowego rekordu urządzenia, co zmarnowałoby zasoby na serwerze i dałoby listę urządzeń zawierających ten sam dwa lub więcej razy. Użytkownicy zobaczyliby więcej niż jednego „iPhone'a Boba” na liście, gdyby ponownie zainstalowali aplikację.

  • Wygenerować UUID i przechowywać w pęku kluczy? To był mój plan, ponieważ utrzymuje się nawet po odinstalowaniu aplikacji. Ale podczas przywracania kopii zapasowej iTunes na nowym urządzeniu z systemem iOS, brelok jest przesyłany, jeśli kopia zapasowa jest zaszyfrowana. Może to prowadzić do dwóch urządzeń zawierających ten sam identyfikator urządzenia, jeśli oba stare i nowe urządzenia są w użyciu. Powinny być wymienione jako dwa urządzenia w usłudze online, nawet jeśli nazwa urządzenia jest taka sama.

  • Wygenerować hash adres MAC i identyfikator pakietu? To wygląda na najlepsze rozwiązanie tego, czego potrzebuję. Przez mieszanie z identyfikatorem pakietu wygenerowany identyfikator urządzenia nie pozwoli na śledzenie urządzenia w aplikacjach i otrzymam unikalny identyfikator dla kombinacji aplikacja + urządzenie.

Warto zauważyć, że własna dokumentacja Apple odnosi się do sprawdzania wpływów z Mac App Store poprzez obliczenie skrótu systemowego adresu MAC oraz identyfikatora i wersji pakietu. Wydaje się, że jest to dozwolone przez zasady, niezależnie od tego, czy przechodzi przez przegląd aplikacji, którego jeszcze nie znam.


10
Aby uniknąć sytuacji opisanej w drugim punkcie, ustaw dostępność elementu pęku kluczy na kSecAttrAccessibleAlwaysThisDeviceOnly. Zapewni to, że Twój identyfikator UUID nie zostanie przywrócony na inne urządzenia, nawet jeśli kopia zapasowa jest zaszyfrowana.
Jeevan Takhar

To jest zachowanie, które widziałem wiele razy. Na przykład rejestruję iPhone'a do Google Sync. Potem mam nowego iPhone'a, zarejestruj go i voila - mam teraz 2 iPhone'y na liście w moich ustawieniach synchronizacji.
Jay Imerman

11

Wygląda na to, że w iOS 6 Apple zaleca korzystanie z klasy NSUUID .

Z wiadomości teraz w dokumentach UIDevice dla uniqueIdentifierwłaściwości:

Przestarzałe w iOS 5.0. Zamiast tego należy odpowiednio użyć właściwości identifierForVendor tej klasy lub właściwości AdvertisingIdentifier klasy ASIdentifierManager lub użyć metody UUID klasy NSUUID, aby utworzyć identyfikator UUID i zapisać go w domyślnej bazie danych użytkownika.


10

Może pomóc: użyj poniższego kodu, zawsze będzie Unikatowy, z wyjątkiem tego, że usuniesz (sformatujesz) urządzenie.

UIDevice *myDevice=[UIDevice currentDevice];
NSString *UUID = [[myDevice identifierForVendor] UUIDString];

1
Użyłem tego kodu. Ale kiedy
usunąłem

1
jest to proste rozwiązanie, jeśli nie potrzebujesz solidnej metody. Używam go teraz w mojej aplikacji.
Reuben L.,

@Durgaprasad: zmieni się zawsze, ponieważ zależy od dostawcy. Na przykład: 1. Jeśli zainstalowałeś aplikację z bundleidenedifier: com.abcd.com =>, to się zmieni. 2. Jeśli zainstalowałeś dwie aplikacje z bundleidenedifier: com.abcd.com => To nie będzie się szarpało (Zachowaj jedną aplikację podczas)
Ashvin Ajadiya

7

Chciałbym również zasugerować zmianie ze uniqueIdentifierdo tej otwartej biblioteki źródłowej (2 proste kategorie naprawdę), które wykorzystują urządzenia adres MAC wraz z App Bundle identyfikatora do wygenerowania unikalnego identyfikatora w aplikacjach, które mogą być używane jako UDID wymiany.

Pamiętaj, że w przeciwieństwie do UDID, liczba ta będzie inna dla każdej aplikacji.

Musisz po prostu zaimportować dołączone NSStringi UIDevicekategorie i zadzwonić w ten [[UIDevice currentDevice] uniqueDeviceIdentifier]sposób:

#import "UIDevice+IdentifierAddition.h"
#import "NSString+MD5Addition.h"
NSString *iosFiveUDID = [[UIDevice currentDevice] uniqueDeviceIdentifier]

Możesz go znaleźć na Github tutaj:

UIDevice z UniqueIdentifier dla iOS 5


Oto kategorie (tylko pliki .m - sprawdź nagłówki w projekcie github):

UIDevice + IdentifierAddition.m

#import "UIDevice+IdentifierAddition.h"
#import "NSString+MD5Addition.h"

#include <sys/socket.h> // Per msqr
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>

@interface UIDevice(Private)

- (NSString *) macaddress;

@end

@implementation UIDevice (IdentifierAddition)

////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark Private Methods

// Return the local MAC addy
// Courtesy of FreeBSD hackers email list
// Accidentally munged during previous update. Fixed thanks to erica sadun & mlamb.
- (NSString *) macaddress{
    
    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("Could not allocate memory. error!\n");
        return NULL;
    }
    
    if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
        printf("Error: sysctl, take 2");
        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;
}

////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark Public Methods

- (NSString *) uniqueDeviceIdentifier{
    NSString *macaddress = [[UIDevice currentDevice] macaddress];
    NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];  
    NSString *stringToHash = [NSString stringWithFormat:@"%@%@",macaddress,bundleIdentifier];
    NSString *uniqueIdentifier = [stringToHash stringFromMD5];  
    return uniqueIdentifier;
}

- (NSString *) uniqueGlobalDeviceIdentifier{
    NSString *macaddress = [[UIDevice currentDevice] macaddress];
    NSString *uniqueIdentifier = [macaddress stringFromMD5];    
    return uniqueIdentifier;
}

@end

NSString + MD5Addition.m:

#import "NSString+MD5Addition.h"
#import <CommonCrypto/CommonDigest.h>

@implementation NSString(MD5Addition)

- (NSString *) stringFromMD5{
    
    if(self == nil || [self length] == 0)
        return nil;
    
    const char *value = [self 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]];
    }
    return [outputString autorelease];
}

@end

3
Począwszy od iOS 7, Apple zwróci stałą wartość adresu MAC. To ma sens. Adres MAC jest wrażliwy.
Roberto


4

Adres MAC może być sfałszowany, co czyni takie podejście bezużytecznym do wiązania treści z konkretnymi użytkownikami lub wdrażania funkcji bezpieczeństwa, takich jak czarne listy.

Po kilku dalszych badaniach wydaje mi się, że na razie nie mamy odpowiedniej alternatywy. Mam poważną nadzieję, że Apple ponownie rozważy swoją decyzję.

Być może dobrym pomysłem byłoby wysłanie wiadomości e-mail do Apple na ten temat i / lub zgłoszenie prośby o błąd / funkcję w tym temacie, ponieważ może nawet nie są świadomi pełnych konsekwencji dla programistów.


13
Ważny punkt, jednak uważam, że UUID można również sfałszować / zamienić na telefon z uszkodzonym więzieniem, więc technicznie istniejący [UIDevice uniqueIdentifier] jest tak samo wadliwy.
Oliver Pearmain,

3
Zawsze możesz utworzyć identyfikator na serwerze i zapisać go na urządzeniu. Tak właśnie robi większość aplikacji. Nie rozumiem, dlaczego programiści iOS potrzebują czegoś specjalnego.
Sulthan

1
@Sulthan nie działa na iOS, ponieważ jeśli odinstalujesz aplikację, wszystkie jej dane znikną, więc nie ma sposobu, aby zagwarantować unikalny identyfikator urządzenia w ten sposób.
lkraider

4
Nie, jeśli zapiszesz go w pęku kluczy. W każdym razie nigdy nie widziałem aplikacji, w której byłby to problem. Jeśli aplikacja i dane zostały usunięte, nie potrzebujesz tego samego identyfikatora urządzenia. Jeśli chcesz zidentyfikować użytkownika, poproś go o e-mail.
Sulthan

Dostęp do adresu MAC został również zablokowany przez Apple w nowej wersji iOS;
Ans

4

UIDevice identifierForVendor wprowadzone w iOS 6 będzie działać na twoje potrzeby.

identifierForVendorto ciąg alfanumeryczny, który jednoznacznie identyfikuje urządzenie od dostawcy aplikacji. (tylko czytać)

@property(nonatomic, readonly, retain) NSUUID *identifierForVendor

Wartość tej właściwości jest taka sama dla aplikacji pochodzących od tego samego dostawcy działającego na tym samym urządzeniu. Inna wartość jest zwracana dla aplikacji na tym samym urządzeniu, które pochodzą od różnych dostawców, oraz dla aplikacji na różnych urządzeniach, niezależnie od dostawcy.

Dostępne w iOS 6.0 i nowszych oraz zadeklarowane w UIDevice.h

W przypadku systemu iOS 5 skorzystaj z tego linku UIDevice-with-UniqueIdentifier-for-iOS-5


4

Korzystanie z SSKeychain i kodu wspomnianego powyżej. Oto kod do skopiowania / wklejenia (dodaj moduł SSKeychain):

+(NSString *) getUUID {

//Use the bundle name as the App identifier. No need to get the localized version.

NSString *Appname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];    

//Check if we have UUID already

NSString *retrieveuuid = [SSKeychain passwordForService:Appname account:@"user"];

if (retrieveuuid == NULL)
{

    //Create new key for this app/device

    CFUUIDRef newUniqueId = CFUUIDCreate(kCFAllocatorDefault);

    retrieveuuid = (__bridge_transfer NSString*)CFUUIDCreateString(kCFAllocatorDefault, newUniqueId);

    CFRelease(newUniqueId);

    //Save key to Keychain
    [SSKeychain setPassword:retrieveuuid forService:Appname account:@"user"];
}

return retrieveuuid;

}


3

Poniższy kod pomaga uzyskać UDID:

        udid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
        NSLog(@"UDID : %@", udid);

3

To jest kod, którego używam, aby uzyskać identyfikator dla iOS 5 i iOS 6, 7:

- (NSString *) advertisingIdentifier
{
    if (!NSClassFromString(@"ASIdentifierManager")) {
        SEL selector = NSSelectorFromString(@"uniqueIdentifier");
        if ([[UIDevice currentDevice] respondsToSelector:selector]) {
            return [[UIDevice currentDevice] performSelector:selector];
        }
    }
    return [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
}

Co robisz z ostrzeżeniem kompilatora PerformSelector may cause a leak because its selector is unknown?
Basil Bourque

Nie możesz już używać identyfikatora reklamowego do tego celu, ponieważ Apple go odrzuci. Więcej informacji: techcrunch.com/2014/02/03/…
codeplasma


2

iOS 11 wprowadził platformę DeviceCheck. Posiada w pełni odporne rozwiązanie do jednoznacznej identyfikacji urządzenia.


1

Działający sposób na uzyskanie UDID:

  1. Uruchom serwer internetowy w aplikacji z dwiema stronami: jedna powinna zwrócić specjalnie spreparowany profil MobileConfiguration, a druga powinna zebrać UDID. Więcej informacji tutaj , tutaj i tutaj .
  2. Otwierasz pierwszą stronę w Mobile Safari z poziomu aplikacji i przekierowuje Cię do Settings.app z prośbą o zainstalowanie profilu konfiguracji. Po zainstalowaniu profilu identyfikator UDID jest wysyłany na drugą stronę internetową i można uzyskać do niego dostęp z poziomu aplikacji. (Settings.app ma wszystkie niezbędne uprawnienia i różne reguły piaskownicy).

Przykład użycia 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)
    }
}

Oto zawartość 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>

Instalacja profilu zakończy się niepowodzeniem (nie zadałem sobie trudu, aby zaimplementować oczekiwaną odpowiedź, zobacz dokumentację ), ale aplikacja otrzyma poprawny identyfikator UDID. I powinieneś także podpisać mobileconfig .


1

W przypadku Swift 3.0 użyj poniższego kodu.

let deviceIdentifier: String = (UIDevice.current.identifierForVendor?.uuidString)!
NSLog("output is : %@", deviceIdentifier)

1
iOS 11 wprowadził platformę DeviceCheck. Posiada w pełni odporne rozwiązanie do jednoznacznej identyfikacji urządzenia.
Santosh Botre

1

Możesz użyć

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

Który jest unikalny dla urządzenia we wszystkich aplikacjach.



0

Jeśli ktoś natknie się na to pytanie, szukając alternatywy. Stosowałem to podejście w IDManagerklasie. Jest to zbiór różnych rozwiązań. KeyChainUtil to opakowanie do odczytu z pęku kluczy. Możesz także użyć tego hashed MAC addressrodzaju jako unikalnego identyfikatora.

/*  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
+ (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;
}

0

Możemy użyć identyfikatora ForVendor dla 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;
}

--Ważna uwaga ---

UDID i identyfikator ForVendor są różne: ---

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.

Jesteś pewny ? czy możemy użyć identyfikatora ForVendor dla iOS7?
Avis

0

Apple ukrył identyfikator UDID przed wszystkimi publicznymi interfejsami API, zaczynając od iOS 7. Każdy identyfikator UDID rozpoczynający się od FFFF jest fałszywym identyfikatorem. Aplikacje „Wyślij UDID”, które wcześniej działały, nie mogą już być używane do gromadzenia UDID dla urządzeń testowych. (westchnienie!)

Identyfikator UDID jest wyświetlany, gdy urządzenie jest podłączone do XCode (w organizatorze) i gdy urządzenie jest podłączone do iTunes (chociaż trzeba kliknąć „Numer seryjny”, aby wyświetlić identyfikator.

Jeśli chcesz uzyskać identyfikator UDID dla urządzenia, aby dodać je do profilu administracyjnego, a nie możesz zrobić tego samodzielnie w XCode, musisz przeprowadzić je przez kroki, aby skopiować / wkleić go z iTunes.

Czy istnieje sposób (od wydania iOS 7), aby uzyskać identyfikator UDID bez używania iTunes na PC / Mac?


0

Miałem też problem, a rozwiązanie jest proste:

    // 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

Niezbyt doskonała, ale jedna z najlepszych i najbliższych alternatyw dla UDID (w Swift z iOS 8.1 i Xcode 6.1):

Generowanie losowego UUID

let strUUID: String = NSUUID().UUIDString

I użyj biblioteki KeychainWrapper :

Dodaj wartość ciągu do pęku kluczy:

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

Pobierz wartość ciągu z pęku kluczy:

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

Usuń wartość ciągu z pęku kluczy:

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

To rozwiązanie korzysta z pęku kluczy, dlatego zapis przechowywany w pęku kluczy zostanie zachowany, nawet po odinstalowaniu i ponownej instalacji aplikacji. Jedynym sposobem usunięcia tego rekordu jest zresetowanie całej zawartości i ustawień urządzenia. Dlatego wspomniałem, że to rozwiązanie zastępowania nie jest idealne, ale pozostaje jednym z najlepszych rozwiązań zastępujących UDID na iOS 8.1 za pomocą Swift.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.