一、核心替代方案的版本演進總覽
|
替代方案
|
iOS 5 及以下
|
iOS 6-iOS 13
|
iOS 14+
|
iOS 17+
|
核心變更點
|
|
UUID+Keychain
|
無限制
|
無限制
|
無限制
|
無限制
|
無核心 API 變更,僅 Keychain 權限微調
|
|
IDFV
|
未推出
|
穩定
|
穩定
|
穩定
|
自 iOS 6 推出後無核心變更
|
|
IDFA
|
未推出
|
無需授權
|
需用户授權
|
授權邏輯不變
|
iOS 14 + 強制 ATT 授權,否則返回 0000-0000
|
二、分方案詳解版本變化
1. UUID + Keychain(最穩定的方案)
|
iOS 版本
|
關鍵變化
|
適配建議
|
|
iOS 3-iOS 7
|
Keychain 默認 |
無需特殊適配,正常使用即可
|
|
iOS 8-iOS 12
|
Apple 強化 Keychain 權限,推薦使用 |
統一使用 |
|
iOS 13+
|
新增 |
如需 iCloud 同步,用 |
|
iOS 17+
|
Keychain 對第三方 App 組(App Group)的訪問限制更嚴格
|
跨 App 共享 UDID 時,需確保 App Group 配置正確,且權限為 |
swift
// 動態適配Keychain權限,兼容所有iOS版本
private static func getKeychainAccessibility() -> CFString {
if #available(iOS 13.0, *) {
// iOS13+推薦:僅當前設備可用,不隨iCloud同步
return kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
} else {
// 低版本兼容
return kSecAttrAccessibleAfterFirstUnlock
}
}
// 使用時替換原權限配置
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key,
kSecValueData: data,
kSecAttrAccessible: getKeychainAccessibility() // 動態適配
]
2. IDFV(Identifier for Vendor)
|
iOS 版本
|
關鍵變化
|
適配建議
|
|
iOS 6-iOS 8
|
卸載開發者所有 App 後,IDFV 立即重置
|
僅用於同一開發者 App 間標識,不依賴其長期穩定
|
|
iOS 9-iOS 17
|
卸載開發者所有 App 後,IDFV 不會立即重置,需設備重啓 / 過一段時間才重置
|
不要將 IDFV 作為 “永久設備標識”,僅作為臨時標識,核心場景仍用 UUID+Keychain
|
|
全版本
|
API 始終是 |
調用時必須判空,兜底生成 UUID(避免極少數情況返回 nil)
|
swift
func getIDFV() -> String {
// 全版本兼容:判空+兜底
guard let idfv = UIDevice.current.identifierForVendor?.uuidString else {
return UUID().uuidString
}
// iOS9+可額外校驗是否為默認值(極少情況)
if #available(iOS 9.0, *) {
if idfv == "00000000-0000-0000-0000-000000000000" {
return UUID().uuidString
}
}
return idfv
}
3. IDFA(Advertising Identifier)
|
iOS 版本
|
關鍵變化
|
適配建議
|
|
iOS 6-iOS 13
|
無需用户授權,直接通過 |
僅需導入 |
|
iOS 14.0-iOS 14.5
|
推出 ATT 框架(AppTrackingTransparency),需請求用户授權才能獲取 IDFA;未授權返回 0000-0000
|
1. 導入
2. Info.plist 添加
3. 調用 |
|
iOS 14.5+
|
強化 ATT 授權:未授權時,不僅 IDFA 返回 0000,還禁止通過其他方式追蹤用户
|
嚴格遵循授權流程,不繞過 ATT 獲取用户標識;僅在廣告場景使用 IDFA
|
|
iOS 17+
|
ATT 授權彈窗調整:增加 “僅本次使用” 選項(部分地區)
|
適配授權狀態的新枚舉值( |
swift
import AdSupport
import AppTrackingTransparency
func requestIDFA(completion: @escaping (String?) -> Void) {
// 1. iOS14+需ATT授權
if #available(iOS 14.0, *) {
ATTrackingManager.requestTrackingAuthorization { status in
DispatchQueue.main.async {
self.handleIDFAStatus(status, completion: completion)
}
}
} else {
// 2. iOS14以下直接獲取
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
completion(idfa == "00000000-0000-0000-0000-000000000000" ? nil : idfa)
}
}
// 處理授權狀態(兼容iOS17+新狀態)
@available(iOS 14.0, *)
private func handleIDFAStatus(_ status: ATTrackingManager.AuthorizationStatus, completion: @escaping (String?) -> Void) {
switch status {
case .authorized:
// 授權成功
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
completion(idfa)
case .denied, .restricted:
// 拒絕/受限
completion(nil)
case .notDetermined:
// 未決定(極少出現)
completion(nil)
// iOS17+新增:僅本次使用(臨時授權)
case .ephemeral where #available(iOS 17.0, *):
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
completion(idfa)
@unknown default:
completion(nil)
}
}
三、跨版本適配的通用原則
總結