调研:iOS 是否存在类似 Android SSAID 那样可获取的标识符 原文标题:I researched whether there are retrievable identifiers in iOS similar to Android's SSAID | DevelopersIO
内容概要总结
Classmethod 工程师撰写的对比文章,回答 Android 开发者常见疑问:iOS 是否有类似 Android SSAID(Android ID)那样在 App 重装后仍能取到同一值的标识符。
核心结论:iOS 没有官方支持的重装后持久化标识符,这是 Apple 隐私优先设计的刻意选择。文章逐项梳理了 iOS 可用的标识符——UDID(已废弃)、IDFA(需 ATT 授权、可重置、整体授权率约 25-35%)、IDFV(按 vendor 共享、删除该 vendor 全部 App 后重置)、DeviceCheck(每设备 2 bit 状态,非标识符)、App Attest(校验 App 完整性)、Firebase FID(重装即变、270 天不活跃会删除)、APNs token(禁止用于追踪)、Keychain UUID(非官方、长期有风险),并给出各自作用域、权限要求、重装后行为与用途的对照表。
文章还解释了 iOS 不提供 SSAID 等价物的原因(删除 App 是用户意图的明确表达,持久 ID 易被广告网络用于跨 App 追踪),并给出「确实需要持久 ID 时」的建议(鼓励注册账号、用 Sign in with Apple / 邮箱 / 手机号),最后提醒 iOS 17 起需在 PrivacyInfo.xcprivacy 中声明标识符使用(隐私清单)。
翻译内容
原文内容(English)
在 iOS App 开发中,有时需要识别设备或用户。我经常从 Android 开发者那里听到一个问题:「iOS 上有没有类似 Android SSAID(Android ID)那样、在 App 重装之后仍然存在的等价物?」
简短的回答是:iOS 并没有官方支持的、在 App 重装后仍然持久的标识符。这是有意为之,源于 Apple 隐私优先的设计。
本文将详细说明 iOS 中可用的标识符类型、它们的获取条件、何时会变化,以及它们与 Android 的差异。
什么是 Android SSAID?
先简要说明一下作为对比基准的 Android SSAID(Settings.Secure.ANDROID_ID,即 Android ID)。
SSAID 是 Android 8.0 及以后版本中可用的设备标识符。它的主要特性是:即使重装 App 也能取到同一个 ID。它不需要用户同意,并且会为每个 App 和签名密钥生成不同的 ID。除非设备被重置或签名密钥发生变化,该 ID 会一直持续。
https://developer.android.com/reference/android/provider/Settings.Secure#ANDROID_ID
这种「无需用户同意」且「重装后 ID 不变」的特性,在 iOS 上并不存在。
iOS 中可用的标识符一览
下表汇总了 iOS 中可用的主要标识符。
| 标识符 | 作用域 | 用户权限 | 重装后 | 主要用途 |
|---|---|---|---|---|
| UDID | 整台设备 | 不需要 | - | 已废弃 |
| IDFA | 整台设备 | 需要 ATT 权限[1] | 可更改 | 广告追踪 |
| IDFV | 按 vendor | 不需要 | 会变化[2] | 分析/认证 |
| DeviceCheck | 整台设备 | 不需要 | 持久(2 bit) | 欺诈检测 |
| App Attest | 按 App | 不需要 | 持久 | 完整性校验 |
| Firebase FID | 按 App | 不需要 | 会变化 | Firebase 内部使用 |
| APNs token | 按 App | 不需要 | 可能变化 | 推送通知 |
| Keychain UUID | 可配置 | 不需要 | 持久(非官方) | 自定义用途 |
1. UDID(已废弃)
UDID(Unique Device Identifier)是曾经存在过的设备专属标识符。它在 iOS 5(2011 年)被废弃,于 2013 年 5 月被 App Store 禁止,其 API 在 iOS 7 中被完全移除。
NSString *udid = [[UIDevice currentDevice] uniqueIdentifier];
它被停用,是因为广告网络滥用它进行跨 App 追踪、侵犯用户隐私,并且用户没有办法重置自己的 ID。目前,使用 UDID 的 App 在 App Store 审核中会被立即拒绝。停用消息曾发布于 Apple Developer News。
在 UDID 被废弃之后,使用 MAC 地址一度是变通办法,但自 iOS 7 起它返回固定值,到 2025 年已不可用。
2. IDFV(Identifier for Vendor)
IDFV 是目前最常用的标识符,无需用户同意或 ATT 权限即可获取。它在同一 vendor(开发者)的 App 之间共享。
let idfv = UIDevice.current.identifierForVendor?.uuidString
// 示例:95290AB7-FC58-4CC9-8B86-F8F38505B512
如果用户删除了同一 vendor 的全部 App,IDFV 会重置。这意味着它不具备 Android SSAID 那样完全的持久性。如果某个 vendor 只发布了一个 App,那么重装后 IDFV 就会变化。
IDFV 的唯一性由 Bundle Identifier 的前两个分量决定(例如 com.example)。关于详细规范和变化的时机,请参考 Apple 官方文档。
3. IDFA(Identifier for Advertisers)
IDFA 是广告标识符,但自 iOS 14.5 起必须获得 App Tracking Transparency(ATT)权限。你需要在 Info.plist 中设置 NSUserTrackingUsageDescription。
import AdSupport
import AppTrackingTransparency
ATTrackingManager.requestTrackingAuthorization { status in
if status == .authorized {
let idfa = ASIdentifierManager.shared().advertisingIdentifier
print("IDFA: \(idfa.uuidString)")
}
}
如果用户不允许追踪,会返回零值(00000000-0000-0000-0000-000000000000)。ATT 同意弹窗每个 App 只显示一次,根据 Flurry 的研究,整体授权率约为 25-35%。你应当假定无法从所有用户那里获取到它。
由于 IDFA 可由用户随时重置,Apple 的文档建议每次使用时都重新获取,而不是存储它。详情请参考 Apple 官方文档。
4. DeviceCheck
DeviceCheck 是一个允许开发者(vendor)在 Apple 服务器上为每台设备存储 2 bit(4 种状态)数据的框架。请注意,这 2 bit 在同一开发者的多个 App 之间是共享的。数据在 App 删除后依然持久,但这是用于状态存储的 API,而不是标识符。
import DeviceCheck
let tokenData = try await DCDevice.current.generateToken()
let token = tokenData.base64EncodedString()
// 将此 token 发送到你的服务器,由服务器与 Apple API 交互
生成的 token 是临时的,每次返回的值都不同。由于需要服务器端与 Apple API 交互才能读写这 2 bit,它不能用于唯一标识一台设备。其主要用途是欺诈检测和一次性权益管理。详情见 Apple 官方文档。
5. App Attest
App Attest 是用于校验 App 完整性的框架(iOS 14+)。它在 Secure Enclave 中生成密码学密钥,并让 Apple 证明某个 App 实例是合法的。
import DeviceCheck
let service = DCAppAttestService.shared
guard service.isSupported else { return }
let keyId = try await service.generateKey()
let attestation = try await service.attestKey(keyId, clientDataHash: serverChallenge)
// 在服务器端验证 attestation
与 DeviceCheck 一样,服务器端验证是强制性的,并且它在模拟器中无法工作。其主要用途是 API 保护和阻止被篡改 App 的未授权访问。它并非设计用来充当标识符。
与 DeviceCheck 的区别在于:DeviceCheck 是「用 2 bit 存储设备状态」,而 App Attest 是「证明 App 未被篡改」。二者经常互补使用。详情见 Apple 官方文档。
6. Firebase Installation ID(FID)
这是使用 Firebase 时自动生成的标识符。
import FirebaseInstallations
let id = try await Installations.installations().installationID()
print("Firebase Installation ID: \(id)")
FID 在 App 重装或缓存清除时会变化,因此不能替代 SSAID。此外,正如 Firebase 文档所述,若 270 天不活跃,它会在后端被删除。它是 Firebase 内部使用的标识符,不适合用于持久化的设备识别。
7. APNs Device Token
这是用于推送通知的 token。禁止将其用于追踪目的。
// AppDelegate.swift
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
print("APNs Token: \(token)")
}
Apple 官方文档建议「不要把 device token 缓存在本地,而是在需要时从系统获取」,因为该 token 在操作系统升级或设备恢复时会发生变化。
8. Keychain UUID
这种方法是指自己生成一个 UUID 并存储到 Keychain 中。虽然技术上可行,但它并非官方方法,因此我略去实现细节。
这种做法存在若干问题。首先,它不是官方 API,未来可能因 Apple 的规范变更而失效。其次,如果被判定为用于追踪目的,存在被 App Store 拒绝的风险。此外,取决于 kSecAttrAccessible 设置,它可能会在设备备份中被携带过去。
在 iOS 10.3 Beta 中,Apple 曾尝试做出一项规范变更,即「App 被删除时删除其 Keychain 数据」。Apple Developer Forums 表示:「这是 iOS 10.3 中为保护用户隐私而做出的有意变更。可识别用户的信息,不应在该信息的创建 App 被删除后仍留在设备上。」然而,该变更在 Beta 7 中被撤回,原有行为(不删除 Keychain 数据)得以恢复。
截至 2025 年 12 月(iOS 26.1),该行为仍在继续,但 Apple 在另一个帖子中表示:「Keychain 文档从未规定过这种情况下会发生什么」,「Keychain 条目在 App 删除后仍然持久,从来不是 API 契约的一部分,而一直只是一个实现细节。」换言之,「重装后不被删除」并非一项规范,而只是它历史上的行为方式。如果你依赖这一行为,就需要在设计 App 时考虑到它未来可能变化。
为什么 iOS 没有 SSAID 的等价物?
阅读 Apple 的隐私政策和 App Store 审核指南,这看来是刻意的设计。
删除 App 是用户意图「我想和这个 App 断绝关系」的明确表达。一个在重装后仍能追踪用户的标识符,与这一意图相悖。此外,持久 ID 可能被广告网络滥用于跨 App 追踪。IDFA 虽然存在,但自 iOS 14.5 起需要 ATT 权限,且随时可被重置,从而确保了用户的选择权。
Apple 优先考虑用户控制,其设计理念尊重用户从设置 App 重置广告 ID 的能力、通过删除 App 彻底删除数据的权利,即所谓的「被遗忘权」。
如果确实需要持久 ID 怎么办?
最合适的做法是鼓励用户创建账号。登录方式应尽可能简单,因此推荐使用 Sign in with Apple 或邮箱/手机号认证。自行生成 ID 并存入 Keychain 也是一种选择,但如前所述,它短期内可行,却带有长期风险。
别忘了处理隐私清单(Privacy Manifests)
在本文中,我谈到了 Apple 隐私优先的设计理念。开发 iOS App 时,务必不要忘记「Privacy Manifests(隐私清单)」。你必须清楚地向用户披露你的 App 需要哪些权限,以及它出于什么目的使用各类数据。
自 iOS 17 起,所有 App 和 SDK 都需要在 PrivacyInfo.xcprivacy 文件中声明其对标识符的使用。详情请参考我此前写的一篇文章:
https://dev.classmethod.jp/articles/support-privacy-manifests-for-ios-app/
不过,截至 2025 年 12 月,虽然包含 PrivacyInfo.xcprivacy 的 App 会被严格检查,但没有它的 App 仍能通过审核。我认为隐私清单制度不会被废止。为避免 App 审核被突然拒绝,最好对任何不合规的 App 都加以处理。
总结
iOS 中没有 SSAID 的等价物。这是 Apple 隐私优先设计的结果,是有意不提供的。
把依赖 SSAID 的 Android 设计移植到 iOS 时,实现一套用户账号体系是最可靠的做法。如前所述,Keychain UUID 短期内可行,但带有长期风险。
以下是常见用例及其应使用的标识符汇总:
| 用例 | 推荐标识符 |
|---|---|
| 重装后持久化 | ❌ 官方不存在 → 推荐用户账号 |
| App 内分析 | ✅ IDFV |
| 广告追踪 | ✅ IDFA(需 ATT 权限) |
| 欺诈检测 | ✅ DeviceCheck |
| App 完整性校验 | ✅ App Attest |
iOS 的隐私模型与 Android 有根本差异。与其说「iOS 没有 SSAID 等价物真不方便」,不如转变思路,「按照 iOS 的隐私模型来设计」。
由于本文主要讨论 iOS,我未作详细展开,但即便在 Android 上,也建议广告用途使用 Advertising ID、App 分析使用 App Set ID、非广告用途使用 FID 或 GUID,这表明业界正在远离持久化标识符。
https://developer.android.com/identity/user-data-ids?hl=ja
尊重用户隐私的设计,从长远看会赢得用户信任,并促成 App 的成功。请为你的目的选择合适的标识符。
参考资料
- Apple Developer - Protecting the User's Privacy
招聘信息:Classmethod 正在招募 iOS 工程师
Starbucks Digital Technology Department 正在寻找能够开发 iOS 应用的工程师。我们期待那些愿意一边在 misc-ios 等渠道分享新 iOS 支持、一边共同工作的人的申请!
https://careers.classmethod.jp/requirements/sbj-nativeapp-ios/
此外,我们正在制造业领域招募 iOS/Android 首席工程师。让我们一起聊聊移动 App 开发吧!
https://careers.classmethod.jp/requirements/mbt-mobile-lead-engineer/
[1] 可在所有已授予 ATT 权限的 App 之间使用同一个 ID。
[2] 当该 vendor 的全部 App 被删除时,IDFV 会重置。如果某个 vendor 只有一个 App,那么重装后它就会变化。
In iOS app development, there may be a need to identify devices or users. A question I often hear from Android developers is: "Is there an iOS equivalent to Android's SSAID (Android ID) that persists after app reinstallation?"
The short answer is: iOS does not have an officially supported identifier that persists after app reinstallation. This is intentional, due to Apple's privacy-first design.
This article explains in detail the types of identifiers available in iOS, their acquisition conditions, when they change, and how they differ from Android.
What is Android SSAID?
Let me briefly touch on Android's SSAID (Settings.Secure.ANDROID_ID or Android ID), which serves as our point of comparison.
SSAID is a device identifier available in Android 8.0 and later. Its main feature is that the same ID can be retrieved even after reinstalling an app. It doesn't require user consent, and different IDs are generated for each app and signing key. The ID persists unless the device is reset or the signing key changes.
https://developer.android.com/reference/android/provider/Settings.Secure#ANDROID_ID
This characteristic of "no user consent required" and "same ID after reinstallation" doesn't exist in iOS.
List of Available Identifiers in iOS
Here's a table summarizing the main identifiers available in iOS.
| Identifier | Scope | User Permission | After Reinstallation | Main Use |
|---|---|---|---|---|
| UDID | Entire device | Not required | - | Deprecated |
| IDFA | Entire device | ATT permission required[1] | Can be changed | Ad tracking |
| IDFV | Per vendor | Not required | Changes[2] | Analytics/Authentication |
| DeviceCheck | Entire device | Not required | Persistent (2 bits) | Fraud detection |
| App Attest | Per app | Not required | Persistent | Integrity verification |
| Firebase FID | Per app | Not required | Changes | Internal Firebase use |
| APNs token | Per app | Not required | May change | Push notifications |
| Keychain UUID | Configurable | Not required | Persistent (unofficial) | Custom uses |
1. UDID (Deprecated)
UDID (Unique Device Identifier) was a device-specific identifier that once existed. It became deprecated in iOS 5 (2011), was banned from the App Store in May 2013, and its API was completely removed in iOS 7.
NSString *udid = [[UIDevice currentDevice] uniqueIdentifier];
It was discontinued due to abuse by ad networks for cross-app tracking, invasion of user privacy, and the lack of a way for users to reset their IDs. Currently, apps using UDID are immediately rejected in App Store reviews. The discontinuation was announced in Apple Developer News.
After UDID was deprecated, using MAC addresses was temporarily a workaround, but since iOS 7, it returns a fixed value, making it unusable as of 2025.
2. IDFV (Identifier for Vendor)
IDFV is currently the most commonly used identifier, available without user consent or ATT permission. It's shared among apps from the same vendor (developer).
let idfv = UIDevice.current.identifierForVendor?.uuidString
// Example: 95290AB7-FC58-4CC9-8B86-F8F38505B512
If a user deletes all apps from the same vendor, the IDFV resets. This means it doesn't have the complete persistence of Android's SSAID. If a vendor has only one app released, the IDFV changes after reinstallation.
IDFV uniqueness is determined by the first two components of the Bundle Identifier (e.g., com.example). For detailed specifications and timing of changes, please refer to Apple's official documentation.
3. IDFA (Identifier for Advertisers)
IDFA is an advertising identifier, but since iOS 14.5, App Tracking Transparency (ATT) permission is mandatory. You need to set NSUserTrackingUsageDescription in Info.plist.
import AdSupport
import AppTrackingTransparency
ATTrackingManager.requestTrackingAuthorization { status in
if status == .authorized {
let idfa = ASIdentifierManager.shared().advertisingIdentifier
print("IDFA: \(idfa.uuidString)")
}
}
If the user doesn't allow tracking, a zero value (00000000-0000-0000-0000-000000000000) is returned. The ATT consent dialog is displayed only once per app, and according to Flurry's research, the overall permission rate is about 25-35%. You should assume you can't obtain it from all users.
Since IDFA can be reset by users at any time, Apple's documentation recommends accessing it for each use rather than storing it. For details, refer to Apple's official documentation.
4. DeviceCheck
DeviceCheck is a framework that allows developers (vendors) to store 2 bits (4 states) of data on Apple's servers per device. Note that these 2 bits are shared across multiple apps from the same developer. Data persists even after app deletion, but this is an API for state storage, not an identifier.
import DeviceCheck
let tokenData = try await DCDevice.current.generateToken()
let token = tokenData.base64EncodedString()
// Send this token to your server, which interacts with the Apple API
The generated token is temporary, with a different value returned each time. Since it requires server-side interaction with Apple's API to read and write the 2 bits, it can't be used to uniquely identify a device. Its main uses are fraud detection and one-time benefit management. For details, see Apple's official documentation.
5. App Attest
App Attest is a framework for verifying app integrity (iOS 14+). It generates cryptographic keys in the Secure Enclave and allows Apple to certify that an app instance is legitimate.
import DeviceCheck
let service = DCAppAttestService.shared
guard service.isSupported else { return }
let keyId = try await service.generateKey()
let attestation = try await service.attestKey(keyId, clientDataHash: serverChallenge)
// Verify attestation on the server
Like DeviceCheck, server-side verification is mandatory, and it doesn't work in simulators. Its main uses are API protection and preventing unauthorized access from tampered apps. It's not designed to be used as an identifier.
The difference from DeviceCheck is that DeviceCheck "stores device state in 2 bits," while App Attest "proves that an app hasn't been tampered with." They're often used complementarily. For details, see Apple's official documentation.
6. Firebase Installation ID (FID)
This is an automatically generated identifier when using Firebase.
import FirebaseInstallations
let id = try await Installations.installations().installationID()
print("Firebase Installation ID: \(id)")
FID changes when the app is reinstalled or cache is cleared, so it's not a substitute for SSAID. Also, as stated in the Firebase documentation, it's deleted on the backend if inactive for 270 days. It's an identifier used internally by Firebase and is not suitable for persistent device identification.
7. APNs Device Token
This is a token for push notifications. It's prohibited to use it for tracking purposes.
// AppDelegate.swift
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
print("APNs Token: \(token)")
}
Apple's official documentation recommends "not caching the device token locally, but obtaining it from the system when needed" because the token changes during OS updates or device restoration.
8. Keychain UUID
This method involves generating your own UUID and storing it in the Keychain. While technically possible, it's not an official method, so I'll omit the implementation details.
There are several issues with this approach. First, it's not an official API and may stop working in the future due to Apple's specification changes. Also, there's a risk of App Store rejection if it's deemed to be for tracking purposes. Furthermore, depending on the kSecAttrAccessible setting, it might be carried over in device backups.
In iOS 10.3 Beta, Apple once attempted a specification change to "delete Keychain data when the app is deleted." The Apple Developer Forums stated: "This is an intentional change in iOS 10.3 to protect user privacy. Information that can identify a user should not be left on the device after the app that created that information is deleted." However, this change was withdrawn in Beta 7, and the original behavior (Keychain data is not deleted) was restored.
As of December 2025 (iOS 26.1), this behavior continues, but Apple has stated in another thread that "the Keychain documentation has never specified what happens in this case" and "the persistence of Keychain items after app deletion was never part of the API contract, but always an implementation detail." In other words, "not being deleted after reinstallation" is not a specification, but just how it has behaved historically. If you depend on this behavior, you need to design your app considering that it may change in the future.
Why Doesn't iOS Have an SSAID Equivalent?
Reading Apple's privacy policy and App Store review guidelines, this appears to be intentional design.
Deleting an app is a clear expression of the user's intention: "I want to cut ties with this app." An identifier that can track users after reinstallation goes against this intention. Also, persistent IDs can be abused by ad networks for cross-app tracking. IDFA exists, but since iOS 14.5, it requires ATT permission and can be reset at any time, ensuring user choice.
Apple prioritizes user control, with a design philosophy that respects the ability to reset ad IDs from the settings app, the right to completely delete data by deleting the app, the so-called "right to be forgotten."
What if You Absolutely Need a Persistent ID?
The most appropriate approach is to encourage users to create accounts. The login method should be as simple as possible, so using Sign in with Apple or email/phone number authentication is recommended. While generating your own ID and storing it in the Keychain is another option, as mentioned earlier, it works in the short term but carries long-term risks.
Don't Forget to Address Privacy Manifests
Throughout this article, I've touched on Apple's privacy-first design philosophy. When developing iOS apps, it's essential not to forget about "Privacy Manifests." You must clearly disclose to users what permissions your app needs and for what purposes it uses various data.
Since iOS 17, all apps and SDKs need to declare their use of identifiers in the PrivacyInfo.xcprivacy file. For details, please refer to the article I wrote a while back:
https://dev.classmethod.jp/articles/support-privacy-manifests-for-ios-app/
However, as of December 2025, while apps incorporating PrivacyInfo.xcprivacy are strictly checked, apps without it still pass review. I don't think the privacy manifest system will be abolished. To avoid sudden rejection of app reviews, it's best to address this for any non-compliant apps.
Summary
There is no SSAID equivalent in iOS. This is a result of Apple's privacy-first design and is intentionally not provided.
When porting Android designs that depend on SSAID to iOS, implementing a user account system is the most reliable approach. As mentioned earlier, Keychain UUID works in the short term but carries long-term risks.
Here's a summary of common use cases and which identifiers to use:
| Use Case | Recommended Identifier |
|---|---|
| Persistence after reinstallation | ❌ Doesn't officially exist → User account recommended |
| In-app analytics | ✅ IDFV |
| Ad tracking | ✅ IDFA (ATT permission required) |
| Fraud detection | ✅ DeviceCheck |
| App integrity verification | ✅ App Attest |
iOS's privacy model is fundamentally different from Android's. Rather than saying "it's inconvenient that there's no SSAID equivalent in iOS," a shift in thinking to "design according to iOS's privacy model" is necessary.
Since this article primarily deals with iOS, I haven't gone into detail, but even on Android, Advertising ID is recommended for advertising purposes, App Set ID for app analytics, and FID or GUID for non-advertising purposes, suggesting a move away from persistent identifiers.
https://developer.android.com/identity/user-data-ids?hl=ja
Designs that respect user privacy will gain user trust in the long run and lead to app success. Please choose the appropriate identifier for your purpose.
References
Job Information: Classmethod is Recruiting iOS Engineers
Starbucks Digital Technology Department is looking for engineers who can develop iOS applications. We're waiting for applications from people who want to work together while sharing new iOS support on misc-ios and more!
https://careers.classmethod.jp/requirements/sbj-nativeapp-ios/
Additionally, we're recruiting iOS/Android lead engineers in the manufacturing sector. Let's talk about mobile app development together!
https://careers.classmethod.jp/requirements/mbt-mobile-lead-engineer/
A common ID can be used across ATT-permitted apps ↩︎
IDFV resets when all apps from that vendor are deleted. If a vendor has only one app, it changes after reinstallation ↩︎