不推荐使用UIDevice uniqueIdentifier-现在该怎么办?


501

刚刚发现,iOS 5中不推荐使用UIDevice uniqueIdentifier属性,而iOS 7及更高版本中不提供属性。似乎没有其他可用的方法或属性。

我们许多现有的应用程序都紧密依赖此属性来唯一标识特定设备。我们将如何处理这个问题?

2011-2012文档中的建议是:

特别注意事项

不要使用uniqueIdentifier属性。要创建特定于您的应用的唯一标识符,您可以调用CFUUIDCreate函数以创建一个UUID,然后使用NSUserDefaults该类将其写入默认数据库。

但是,如果用户卸载并重新安装该应用程序,则此值将不同。


1
对于仍在使用uniqueIdentifier的应用程序,iOS7现在返回FFFFFFFF + identifierForVendor,这破坏了许多写得不好的非续订订阅应用程序。
Rhythmic Fistman

如果运气应用程序使用推送通知,您可以使用来自苹果的推送服务的令牌发送回,这是每个设备的独特以及
克林赤土

@CalinChitu如果用户不接受推送通知,您是否还会为该用户获得pushID?
Chase Roberts

Answers:


272

如果用户卸载并重新安装该应用程序,则由创建的UUID CFUUIDCreate 唯一的:您每次都会获得一个新的UUID 。

但是您可能希望它不是唯一的,即在用户卸载并重新安装应用程序时,它应该保持不变。这需要一些努力,因为最可靠的每个设备标识符似乎是MAC地址。您可以查询MAC并将其用作UUID。

编辑:当然,您需要始终查询同一接口的MAC。我猜最好的选择是en0。即使接口没有IP /已关闭,MAC也始终存在。

编辑2:正如其他人所指出的那样,自iOS 6起首选的解决方案是-[UIDevice identifierForVendor]。在大多数情况下,您应该可以将其用作旧版本的替代品-[UIDevice uniqueIdentifier](但Apple似乎希望您使用该应用程序在首次启动时创建的UUID)。

编辑3:因此,这个要点不会在注释噪音中迷失:不要将MAC用作UUID,使用MAC创建哈希。即使在重新安装和应用程序之间,该哈希每次都会始终产生相同的结果(如果哈希以相同的方式完成)。无论如何,如今(2013年)不再需要此功能,除非在iOS <6.0上需要“稳定”的设备标识符。

编辑4:在iOS 7中,Apple现在在查询MAC时总是返回固定值,以专门阻止MAC作为ID方案的基础。因此,您现在确实应该使用-[UIDevice identifierForVendor]或创建每次安装的UUID。


8
mac地址是否会根据用户是否通过wifi连接而改变?
奥利弗·皮尔曼

1
@DarkDust:但是由于当您从wifi切换到蜂窝调制解调器时,活动接口会更改,因此活动接口的MAC地址也应更改;除非您始终选择一个特定的接口来获取MAC
2011年

3
@Roger Nolan:请不要编辑其他人的答案,也不要添加看起来像来自原始作者的东西。谢谢。
DarkDust 2013年

2
@RogerNolan只要帖子不是社区答案,则所做的修改仅用于纠正错误和此类问题,而不是用于添加新内容。您有获得特权的原因。想象一下,我编辑了您的答案并写了一些BS,人们会认为您已经写了。我怀疑您是否想这样做:-)但是您不会收到有关编辑已发生的通知,我只是偶然发现的。
DarkDust 2013年

3
苹果现在拒绝使用哈希MAC的应用程序。
伊丹

91

UDID已经可以将替代品用于Apple 。善良的家伙gekitz编写了类别,UIDevice该类别将UDID基于设备的mac地址和捆绑包标识符生成某种形式。

您可以在github上找到代码


3
此实现对于重新安装的设备来说是唯一的(MAC地址),例如Apple的uniqueId,但也尊重隐私,对于应用程序也是唯一的(也使用bundleId)... Apple必须在其API中包含恕我直言。 ..而不弃用任何替代方案。
Vincent Guerci 2011年

8
尽管它使用带有广告条款的旧式bsd许可,但是,yuck。
jbtule 2011年

8
对于现在找到此帖子的其他人,自jbtule进行上述评论以来,许可证已更改。
2012年

1
在本讨论提交评论,图书馆作为,是造成了严重的隐私泄露问题,不应该被使用:
威尔

15
从iOS 7开始,系统02:00:00:00:00:00在您要求任何设备上的MAC地址时始终返回该值。查看此处:developer.apple.com/library/prerelease/ios/releasenotes/General/...
Hejazi

61

根据@moonlight提出的链接,我做了几次测试,这似乎是最好的解决方案。正如@DarkDust所说,该方法将检查en0哪个始终可用。
有2个选项:
uniqueDeviceIdentifier(MAC + CFBundleIdentifier的MD5 )
uniqueGlobalDeviceIdentifier(MAC的MD5),这些选项始终返回相同的值。
在我完成的测试下面(使用真实设备):

#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-(飞机模式)UDID
XXXX7dc3c577446a2bcbd77935bdXXXX-(飞机模式)GlobalAppUDID

XXXX21f1f19edff198e2a2356bf4XXXX-(Wi-Fi)删除并重新安装应用后XXXX7dc3c577446a2bcbd77935bdXXXX(Wi-Fi)之后

希望它有用。

编辑:
正如其他人指出,iOS 7中的此解决方案不再有用,因为uniqueIdentifier它不再可用,并且现在查询MAC地址始终返回02:00:00:00:00:00


13
这在iOS7上不起作用,Apple消除了对MAC地址的使用。
萨里姆·西德

@SarimSidd现在有关iOS 7的信息在NDA下,我们在这里不能讨论。

57

看一下这个,

我们可以使用Keychain而不是NSUserDefaultsclass来存储UUID由创建的CFUUIDCreate

通过这种方式,我们可以避免UUID重新安装带来的麻烦,并且UUID即使用户卸载并重新安装,也可以为同一应用程序获得相同的结果。

UUID 将在用户重置设备时重新创建。

我用SFHFKeychainUtils尝试了这种方法,它的工作原理就像一个魅力。


33
此方法可以完全替代UDID。它还具有根据设备格式重新创建标识符的额外好处(例如,如果设备更改了所有者)。但是,请务必注意,如果用户加密备份,则钥匙串可以还原到其他设备。这可能导致多个设备共享同一UUID的情况。为避免这种情况,请将钥匙串项目的可访问性设置为kSecAttrAccessibleAlwaysThisDeviceOnly。这将确保您的UUID不会迁移到任何其他设备。要从其他应用程序访问您的UUID,请使用kSecAttrAccessGroup密钥。
Jeevan Takhar 2012年

您应该在哪个位置(哪个密钥)将UUID存储在密钥链中?
lostintranslation 2014年

哎呀!链接已损坏
COVID19

48

创建您自己的UUID,然后将其存储在钥匙串中。因此,即使您的应用程序被卸载,它仍然存在。在许多情况下,即使用户在设备之间迁移(例如,完全备份和还原到另一台设备),它也将持续存在。

就您而言,它实际上成为唯一的用户标识符。(甚至比设备标识符更好)。

例:

我正在定义用于创建UUIDas 的自定义方法:

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

然后,您可以在KEYCHAIN应用程序首次启动时将其存储在其中。这样,在首次启动后,我们可以简单地从钥匙串中使用它,而无需重新生成它。使用“钥匙串”进行存储的主要原因是:UUID将“钥匙串” 设置为“钥匙串”后,即使用户完全卸载了该应用程序,然后再次安装该应用程序,它也将持续存在。。因此,这是存储它的永久方法,这意味着密钥将一直都是唯一的。

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

在应用程序启动时,包含以下代码:

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

sskeychain下载SSKeychain.m和.h文件, 并将SSKeychain.m和.h文件拖到您的项目中,然后将“ Security.framework”添加到您的项目中。之后使用UUID只需使用:

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

因为identifierForVendor不能正常工作。在某些情况下可以返回nil或0x0。这种方法似乎运行完美
CReaTuS

3
是否有人在卸载/重新安装周期+提交了经过验证的Apple应用程序后验证了它在iOS7上正常工作?
mindbomb

我已经开始使用此解决方案。在2个设备上进行了几次测试(重建,重新安装,设备关闭),结果表明ID相同。iOS 10.3。
deko

16

也许您可以使用:

[UIDevice currentDevice].identifierForVendor.UUIDString

Apple的文档对identifierForVender的描述如下:

对于来自同一设备上运行的同一供应商的应用程序,此属性的值相同。对于来自不同供应商的同一设备上的应用程序,以及与供应商无关的不同设备上的应用程序,将返回不同的值。


好奇,为什么没有人把这个直到最近......现在我看到它的新的iOS 6
詹姆斯Boutcher

1
如果用户更新ios和/或安装新的ios,那么identifierForVendor的值将更改还是保持不变?
Sunil Zalavadiya

1
从同一供应商处删除所有应用程序后,此值将更改。
Mitesh Khatri

14

您可能要考虑使用OpenUDIDwhich来代替deprecated UDID

基本上,要匹配UDID,需要以下功能:

  1. 唯一或足够独特(低概率碰撞可能是可以接受的)
  2. 重启,还原,卸载之间的持久性
  3. 适用于不同供应商的应用程序(可用于通过CPI网络获取用户)-

OpenUDID 可以满足上述要求,甚至还具有内置的退出机制供以后考虑。

检查http://OpenUDID.org,它指向相应的GitHub。希望这可以帮助!

附带说明一下,我会回避任何其他MAC地址。虽然MAC地址看起来像是一种诱人的通用解决方案,但请确保这种低挂的水果被毒害了。MAC地址非常敏感,Apple可能会在您甚至说“提交此应用”之前不赞成使用此地址... MAC网络地址用于对专用局域网(WLAN)或其他虚拟专用网络上的某些设备进行身份验证网络(VPN)。..比以前的UDID更敏感!


我真的很好奇这是如何工作的?该代码是用Objective-C编写的,但是没有其他合适的解决方案可以满足上述要求,那么,什么使该框架与众不同?该框架使用的解决方案也应该可以作为建议的答案发布在这里...
jake_hetfield 2012年

我同意-MAC地址也可以手动配置(“克隆”),尽管大多数情况下不太可能。我必须抗议UDID中的D。这不是设备ID,而是UUID(通用唯一标识符)。Apple出厂时将设备ID标记在ROM中的每个设备上。
杰伊·伊默曼

iOS7的最佳解决方案以及唯一识别设备所需的实际操作
敬请参阅dharankar 2014年

1
不建议使用
OpenUDID

11

我敢肯定,苹果公司已经使许多人为此感到烦恼。我为iOS 开发了一个簿记应用程序,并且具有在线服务来同步在不同设备上所做的更改。该服务维护着所有设备以及需要传播给它们的更改的数据库。因此,了解哪些设备是重要的。我正在使用UIDevice uniqueIdentifier跟踪设备,对于它的价值,这是我的想法。

  • 生成UUID并存储在用户默认设置中?没有好处,因为在用户删除应用程序时,这种情况不会持续。如果以后再安装,则联机服务不应创建新的设备记录,这将浪费服务器上的资源,并给出两次或多次包含相同设备的设备列表。如果用户重新安装该应用程序,将会看到列出的多个“ Bob的iPhone”。

  • 生成一个UUID并存储在钥匙串中?这是我的计划,因为即使卸载该应用程序,它仍然存在。但是,当将iTunes备份还原到新的iOS设备时,如果备份已加密,则将传输钥匙串。如果旧设备和新设备都在使用中,则可能导致两个设备包含相同的设备ID。即使设备名称相同,它们也应在联机服务中列为两个设备。

  • 生成哈希的MAC地址和捆绑包ID?这似乎是我需要的最佳解决方案。通过使用包ID散列,生成的设备ID不会使设备能够跨应用程序进行跟踪,并且我获得了应用程序+设备组合的唯一ID。

有趣的是,Apple自己的文档涉及通过计算系统MAC地址的哈希值,捆绑包ID和版本来验证Mac App Store收据。因此,根据政策,无论是否通过应用程序审核(我尚不知道),这似乎都是允许的。


10
为避免第二点所述的情况,请将钥匙串项目的可访问性设置为kSecAttrAccessibleAlwaysThisDeviceOnly。这将确保即使备份已加密,您的UUID也不还原到其他设备。
Jeevan Takhar '04

这确实是我多次看到的行为。例如,我为我的iPhone注册了Google Sync。然后,我有了新的iPhone,进行注册,然后瞧瞧-我的“同步”设置中现在列出了2部iPhone。
杰伊·伊曼

11

对于iOS 6,Apple建议您使用NSUUID类

现在从UIDevice文档中的消息中获取uniqueIdentifier属性:

在iOS 5.0中已弃用。相应地,使用此类的identifierForVendor属性或ASIdentifierManager类的advertiseIdentifier属性,或使用NSUUID类的UUID方法创建UUID并将其写入用户默认数据库。


10

可能有帮助:使用以下代码,它将保持唯一,除非您擦除(格式化)设备。

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

1
我用了这段代码。但是,当我删除应用程序并再次安装时,我得到了新的ID
Durgaprasad 2013年

1
如果您不需要鲁棒的方法,这是一个简单的解决方案。我现在在我的应用程序中使用它。
Reuben L.

@Durgaprasad:它将始终更改,因为它取决于供应商。例如:1.如果您安装了带有bundleidenedifier的应用程序:com.abcd.com =>,则它将更改。2.如果您安装了两个带有bundleidenedifier的应用程序:com.abcd.com =>那么它将不会更改(在此期间保留任何一个应用程序)
Ashvin Ajadiya 2014年

7

我也建议从切换uniqueIdentifier该开源库(2级简单的类真的),利用该设备的MAC地址与应用程序捆绑标识符一起生成的应用程序,可以用作替代UDID一个唯一的ID。

请记住,与UDID不同,此数字对于每个应用程序都会有所不同。

您只需要导入included NSStringUIDevicecategory并按如下方式调用[[UIDevice currentDevice] uniqueDeviceIdentifier]

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

您可以在Github上找到它:

带有用于iOS 5的UniqueIdentifier的UIDevice


以下是类别(仅.m文件-检查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
从iOS 7开始,Apple将为MAC地址返回一个恒定值。这是很合理的。MAC地址是敏感的。
罗伯托


4

可以欺骗MAC地址,这使得这种方法对于将内容绑定到特定用户或实施安全功能(如黑名单)毫无用处。

经过一些进一步的研究,在我看来,到目前为止,我们还没有适当的选择。我非常希望苹果公司能重新考虑他们的决定。

最好通过电子邮件向Apple发送有关此主题的信息和/或对此提出错误/功能请求,因为也许他们甚至不知道对开发人员的全部后果。


13
但是我认为这是有道理的,UUID也可以在越狱的手机上被欺骗/欺骗,因此从技术上讲,现有的[UIDevice uniqueIdentifier]也存在缺陷。
奥利弗·皮尔曼

3
您始终可以在服务器上创建标识符并将其保存在设备上。这就是大多数应用程序执行此操作的方式。我不明白为什么iOS程序员需要一些特别的东西。
苏珊(Sulthan)2012年

1
@Sulthan在iOS上不起作用,因为如果卸载应用程序,其所有数据都将消失,因此无法保证这种方式的唯一设备标识符。
lkraider 2012年

4
如果将其保存到钥匙串,则不会。无论如何,我从未见过有问题的应用程序。如果应用和数据已删除,则不需要相同的设备标识符。如果要标识用户,请向他发送电子邮件。
苏珊(Sulthan)2012年

苹果在新版本的iOS中也禁止访问MAC地址;
2013年

4

UIDevice identifierForVendor iOS 6中引入的功能可以满足您的需求。

identifierForVendor是一个字母数字字符串,用于唯一标识应用程序供应商的设备。(只读)

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

对于来自同一设备上运行的同一供应商的应用程序,此属性的值相同。对于来自不同供应商的同一设备上的应用程序,以及针对不同供应商的不同设备上的应用程序,将返回不同的值。

在iOS 6.0及更高版本中可用,并在中声明 UIDevice.h

对于iOS 5,请参阅此链接UIDevice-with-UniqueIdentifier-for-iOS-5


4

使用上面提到的SSKeychain和代码。这是要复制/粘贴的代码(添加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

以下代码有助于获取UDID:

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

3

这是我用来获取iOS 5和iOS 6、7的ID的代码:

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

您如何处理编译器警告PerformSelector may cause a leak because its selector is unknown
罗勒·布尔克

您将无法再为此使用adsingIdentifier,因为Apple会拒绝使用它。更多信息:techcrunch.com/2014/02/03/...
codeplasma


2

iOS 11引入了DeviceCheck框架。它具有用于唯一标识设备的全面解决方案。


1

一种获取UDID的有效方法:

  1. 用两个页面在应用程序内部启动Web服务器:一个页面应返回特制的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

对于Swift 3.0,请使用以下代码。

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

1
iOS 11引入了DeviceCheck框架。它具有用于唯一标识设备的全面解决方案。
Santosh Botre

1

您可以使用

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

对于所有应用程序中的设备而言,这都是唯一的。



0

如果有人偶然发现此问题,则在寻找替代方法时。我在IDManager课堂上遵循了这种方法,这是来自不同解决方案的集合。KeyChainUtil是从钥匙串读取的包装。您还可以将hashed MAC address用作唯一ID。

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

我们可以为iOS7 使用identifierForVendor

-(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和identifierForVendor不同:---

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.

你确定吗 ?我们可以为iOS7使用identifierForVendor吗?
阿维斯(Avis)2014年

0

从iOS 7开始,Apple从所有公共API中都隐藏了UDID。任何以FFFF开头的UDID都是伪造的ID。先前无法使用的“发送UDID”应用程序无法再用于收集测试设备的UDID。(叹!)

当设备连接到XCode(在管理器中)以及设备连接到iTunes时,将显示UDID(尽管您必须单击“序列号”才能显示标识符)。

如果您需要获取设备的UDID才能添加到配置文件中,而自己无法在XCode中进行操作,则必须引导他们完成从iTunes复制/粘贴设备的步骤。

自从iOS 7发布以来,是否有一种方法可以在PC / Mac上不使用iTunes的情况下获取UDID?


0

我也遇到了一些问题,解决方案很简单:

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

这不是UDID的最佳选择,但它是最佳和最接近的替代方法之一(在使用iOS 8.1和Xcode 6.1的Swift中):

生成随机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")

此解决方案使用钥匙串,因此即使卸载并重新安装应用程序后,存储在钥匙串中的记录也将保留。删除该记录的唯一方法是重置设备的所有内容和设置。这就是为什么我提到这种替代解决方案并不完美,但仍然是使用Swift的iOS 8.1上替代UDID的最佳解决方案之一。


0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.