UIDevice的uniqueIdentifier已经被弃用 - 现在该怎么做?

511
刚刚得知,在iOS 5中已将UIDevice唯一标识符属性(uniqueIdentifier property)作废,在iOS 7及以上版本中也不再提供该属性的支持。目前似乎没有可用或即将推出的替代方法或属性。

我们许多现有的应用程序都高度依赖此属性来唯一标识特定设备。那么我们该如何解决这个问题呢?

2011-2012年的文档建议:

特别注意事项

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

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


1
对于仍在使用uniqueIdentifier的应用程序,iOS7现在返回FFFFFFFF + identifierForVendor,这破坏了许多编写不良的非续订订阅应用程序。 - Rhythmic Fistman
如果你的应用程序幸运地使用了推送通知,你可以使用从苹果推送服务返回的令牌,它对于每个设备都是唯一的。 - Calin Chitu
如果用户不接受推送通知,您是否仍会为该用户获取推送ID? - Chase Roberts
32个回答

274

CFUUIDCreate创建的UUID,如果用户卸载并重新安装应用程序,则是唯一的:每次都会获得一个新的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 中,为了防止使用 MAC 作为 ID 的基础 方案,Apple 现在始终返回一个固定值。因此,你现在应该使用-[UIDevice identifierForVendor] 或创建一个每次安装都不同的 UUID。


8
无论用户是否通过Wifi连接,MAC地址是否会改变? - Oliver Pearmain
1
有人研究过使用Mac地址的合法性方面吗?如果UUID对于苹果公司的隐私构成问题,那么Mac地址可能会是更大的问题。 - AlfeG
3
@Roger Nolan:请不要编辑他人的答案并添加看起来像是原作者的内容。谢谢。 - DarkDust
2
只要帖子不是社区答案,编辑就是为了纠正错误和类似的事情,而不是添加新内容。你获得特权是有原因的。想象一下,如果我编辑你的答案并写一些胡说八道的东西,人们会认为那是你写的。我怀疑你会喜欢那样 :-) 但你不会被通知有一个编辑发生了,我只是偶然发现的。 - DarkDust
3
苹果现在拒绝使用散列 MAC 的应用程序。 - Idan
显示剩余18条评论

92

现在你可以使用替代Apple UDID的方式了。友好的gekitz编写了一个基于设备mac地址和包标识符生成某种类型的UDIDUIDevice类别。

您可以在github上找到代码。


3
这个实现对于设备来说是唯一的(MAC地址),即使重新安装,也像苹果的uniqueId一样唯一,但它也尊重隐私,对于应用程序也是唯一的(还使用bundleId)......在我看来这是必备的,苹果应该将其包含在API中......而不是无替代方案地弃用。 - Vincent Guerci
8
虽然它使用旧版的 BSD 许可证,带有广告条款,但真恶心。 - jbtule
8
对于现在看到此帖子的其他人,自上述jbtule的评论以来已更改许可证。 - James
1
正如在此提交评论中所讨论的那样,该库目前存在严重的隐私泄漏问题,不应使用: - Will
16
从 iOS 7 开始,在任何设备上获取 MAC 地址时,系统总是返回值为 02:00:00:00:00:00。 点击这里查看:https://developer.apple.com/library/prerelease/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS7.html#//apple_ref/doc/uid/TP40013162-SW34 - Hejazi
显示剩余4条评论

61

根据@moonlight提出的链接,我进行了几项测试,看起来这是最好的解决方法。正如@DarkDust所说,该方法会检查始终可用的en0
有两个选项:
uniqueDeviceIdentifier(MAC地址+CFBundleIdentifier的MD5)
uniqueGlobalDeviceIdentifier(MAC地址的MD5),它们总是返回相同的值。
以下是我进行的测试(使用真实设备):

#import "UIDevice+IdentifierAddition.h"

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

XXXX21f1f19edff198e2a2356bf4XXXX - (WIFI)UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (WIFI)全局应用程序UDID

XXXX21f1f19edff198e2a2356bf4XXXX - (3G)UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (3G)全局应用程序UDID

XXXX21f1f19edff198e2a2356bf4XXXX - (GPRS)UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (GPRS)全局应用程序UDID

XXXX21f1f19edff198e2a2356bf4XXXX - (AirPlane mode)UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (AirPlane mode)全局应用程序UDID

XXXX21f1f19edff198e2a2356bf4XXXX - (Wi-Fi)删除并重新安装应用程序后的UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (Wi-Fi)删除并安装应用程序后的全局应用程序UDID

希望这对您有用。

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


13
这在iOS7上行不通,苹果取消了使用MAC地址的功能。 - Sarim Sidd
@SarimSidd 目前关于iOS 7的信息受到保密协议的限制,我们不能在这里讨论。 - Mat

57

请看这个:

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

通过这种方式,我们可以避免在重新安装时重新创建UUID,即使用户卸载并重新安装,也可以获得相同的UUID

UUID仅在用户重置设备时才会重新创建。

我已经尝试了SFHFKeychainUtils这种方法,它非常好用。


33
这种方法是UDID的可靠替代品。它还具有重新创建标识符的附加好处(例如,如果设备更换所有者)。然而,需要注意的是,如果用户加密其备份,钥匙串可以恢复到其他设备上,这可能导致多个设备共享相同的UUID。为避免这种情况,将您的钥匙串项目的可访问性设置为“kSecAttrAccessibleAlwaysThisDeviceOnly”。这将确保您的UUID不会迁移到任何其他设备。要从其他应用程序访问您的UUID,请使用“kSecAttrAccessGroup”键。 - Jeevan Takhar
你应该使用哪个键来将UUID存储在钥匙串中? - lostintranslation
糟糕!链接已损坏。 - Hitesh Surani

48
创建自己的UUID并将其存储在Keychain中,这样即使应用被卸载,它也会持续存在。在许多情况下,即使用户在设备之间迁移(例如完整备份和恢复到另一个设备),该UUID也会持久存在。
实际上,该UUID成为您关注的唯一用户标识符(甚至比设备标识符更好)。
示例: 我正在定义一个用于创建UUID的自定义方法:
- (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文件,将它们拖到你的项目中,并在项目中添加“Security.framework”。 之后要使用UUID,只需简单地使用:

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

因为 identifierForVendor 并不完美地工作。在某些情况下可能会返回 nil 或 0x0。这种方法似乎是完美的。 - CReaTuS
3
有人验证过在iOS7上进行卸载/重新安装后是否有效,并且已经验证了苹果应用提交吗? - mindbomb
我已经开始使用这个解决方案。在两台设备上进行了多次测试(重建、重新安装、设备关机),结果显示id是相同的。iOS 10.3。 - deko

16

也许您可以使用:

[UIDevice currentDevice].identifierForVendor.UUIDString

苹果的文档描述identifierForVender如下:

该属性的值对于来自同一供应商并在同一设备上运行的应用程序相同。 对于来自不同供应商且在同一设备上的应用程序以及对于不考虑供应商的不同设备上的应用程序,返回不同的值。


好奇为什么直到最近才有人提起这个问题...现在我看到它是iOS 6的新功能。 - James Boutcher
1
如果用户更新iOS和/或安装新的iOS,那么identifierForVendor的值会改变还是保持不变? - Sunil Zalavadiya
1
移除同一供应商的所有应用程序后,此值将发生更改。 - Mitesh Khatri

14

您可能想考虑使用OpenUDID,它可以替代已弃用的UDID

基本上,为了匹配UDID,需要具备以下功能:

  1. 唯一或足够唯一(低概率冲突可能非常可接受)
  2. 在重新启动、恢复、卸载后保持不变
  3. 在不同供应商的应用程序之间可用(通过CPI网络获取用户时很有用)-

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

请查看http://OpenUDID.org,它指向相应的GitHub。 希望这能有所帮助!

顺便说一句,我会避免使用任何MAC地址替代方案。虽然MAC地址看起来像是一个诱人且通用的解决方案,但请确保这个简单易实现的方法是“有毒的水果”。MAC地址非常敏感,苹果公司很可能在您提交应用之前就废除对其的访问... MAC网络地址用于验证专用LAN(WLAN)或其他虚拟专用网络(VPN)上的某些设备... 它甚至比以前的UDID更敏感!


我真的很好奇这是如何工作的?代码是用Objective-C编写的,但是没有其他适合上述要求的好解决方案,那么这个框架有什么不同之处? 该框架使用的解决方案也应该可以发布为建议答案... - jake_hetfield
我同意 - MAC地址也可以手动配置(“克隆”),尽管大多数情况下不太可能。 我必须反对UDID中的D。这不是设备ID,而是UUID(通用唯一标识符)。设备ID由苹果在ROM上为每个设备打印。 - Jay Imerman
最佳解决方案适用于iOS7,同时确保能够唯一地识别设备所需的内容。 - vishal dharankar
1
OpenUDID已被弃用,不建议使用。 - mkll

11

看起来对于iOS 6,苹果建议您使用NSUUID类

从现在在UIDevice文档中的消息来看,有关uniqueIdentifier属性:

iOS 5.0中已弃用。改用此类的identifierForVendor属性或ASIdentifierManager类的advertisingIdentifier属性(根据情况选择),或使用NSUUID类的UUID方法创建UUID并将其写入用户默认数据库。


11
我相信苹果的这个改变已经让很多人感到恼火。我为iOS开发了一款记账应用,并提供一个在线服务来同步在不同设备上进行的更改。该服务维护了所有设备和需要传播到它们的更改的数据库。因此,知道哪些设备是哪些设备非常重要。我使用UIDevice uniqueIdentifier来跟踪设备,并且就此发表了一些想法。
  • 生成UUID并存储在用户默认设置中? 这样做不好,因为当用户删除应用程序时,它不会持久化。如果他们稍后重新安装在线服务,则不应创建新的设备记录,这将浪费服务器资源并给出包含同一设备两次或更多次的设备列表。如果用户重新安装应用程序,他们将看到多个“Bob's iPhone”。

  • 生成UUID并存储在钥匙串中? 这是我的计划,因为即使卸载应用程序,它也会持久存在。但是,如果备份加密,则在将iTunes备份恢复到新的iOS设备时,钥匙串也会被转移。如果旧设备和新设备都在使用,则可能导致两个包含相同设备ID的设备。即使设备名称相同,它们也应在在线服务中列为两个设备。

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

值得注意的是,苹果自己的文档提到通过计算系统MAC地址加上bundle id和版本来验证Mac App Store收据。因此,无论是否经过应用程序审查,这似乎都是允许的政策。

10
为避免您第二点所描述的情况,请将您的钥匙串项目的可访问性设置为“kSecAttrAccessibleAlwaysThisDeviceOnly”。这将确保您的UUID不会恢复到其他设备,即使备份已加密。 - Jeevan Takhar
这确实是我多次见过的行为。例如,我为Google Sync注册了我的iPhone。然后我换了一部新的iPhone,注册它,然后哇 - 我现在在我的同步设置中列出了2个iPhone。 - Jay Imerman

10

可能有帮助:

使用以下代码,它将始终是唯一的,除非您擦除(格式化)设备。

Objective-C

选项1:这将在每次安装时更改。

UIDevice *uuid = [NSUUID UUID].UUIDString;

选项2:这将对每个供应商/开发者的Apple帐户是独一无二的。

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

Swift 5.X:

选项 1:这将在每次安装时更改。

let uuid = UUID().uuidString

选项2:这将对每个供应商/开发者的苹果帐户是唯一的。

let myDevice = UIDevice.current
let uuid = myDevice.identifierForVendor?.uuidString

1
我使用了这段代码。但是当我删除应用程序并重新安装后,我得到了新的ID。 - Durgaprasad
1
这是一个简单的解决方案,如果你不需要一个强大的方法。我现在正在我的应用程序中使用它。 - Reuben L.
@Durgaprasad:它将始终更改,因为它取决于供应商。 例如:
  1. 如果您安装了一个带有bundleidenedifier:com.abcd.com的应用程序 =>那么它将更改。
  2. 如果您安装了两个带有bundleidenedifier:com.abcd.com的应用程序 =>那么它将不会更改(保留其中一个应用程序)。
- Ashvin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接