一、核心前提:Apple 已禁用直接獲取 UDID
二、合規的 UDID 替代方案(推薦)
1. UUID + Keychain 存儲(最常用)
- 首次啓動 App 時生成一個隨機的 UUID(通用唯一識別碼);
- 將 UUID 存儲到 Keychain(鑰匙串)中(Keychain 在 App 卸載後仍保留,重裝 App 可恢復);
- 後續每次啓動 App,先從 Keychain 讀取,無則重新生成。
swift
import Foundation
import Security
class DeviceIdentifierManager {
// Keychain存儲的鍵名
private static let udidKey = "com.yourapp.udid"
/// 獲取設備唯一標識(UUID+Keychain)
static func getDeviceUDID() -> String {
// 1. 先從Keychain讀取
if let existingUDID = readFromKeychain(key: udidKey) {
return existingUDID
}
// 2. 無則生成新的UUID
let newUDID = UUID().uuidString
// 3. 存入Keychain
let _ = saveToKeychain(value: newUDID, key: udidKey)
return newUDID
}
// MARK: - Keychain 操作
private static func saveToKeychain(value: String, key: String) -> Bool {
let data = value.data(using: .utf8)!
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key,
kSecValueData: data,
kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock // 解鎖後可訪問
]
// 先刪除舊值(避免重複)
SecItemDelete(query as CFDictionary)
// 添加新值
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
private static func readFromKeychain(key: String) -> String? {
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key,
kSecReturnData: kCFBooleanTrue!,
kSecMatchLimit: kSecMatchLimitOne
]
var data: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &data)
guard status == errSecSuccess, let resultData = data as? Data else {
return nil
}
return String(data: resultData, encoding: .utf8)
}
}
// 使用示例
let deviceUDID = DeviceIdentifierManager.getDeviceUDID()
print("設備唯一標識:\(deviceUDID)")
- ✅ 合規:符合 Apple 隱私政策,可通過 App Store 審核;
- ✅ 穩定:App 卸載重裝後仍能獲取相同標識(Keychain 不隨 App 卸載刪除);
- ❌ 設備重置(抹掉所有內容)後,Keychain 會清空,標識會變化。
2. Identifier for Vendor (IDFV)
swift
import UIKit
/// 獲取IDFV(廠商標識)
func getIDFV() -> String {
guard let idfv = UIDevice.current.identifierForVendor?.uuidString else {
return UUID().uuidString // 兜底生成UUID
}
return idfv
}
// 使用示例
let idfv = getIDFV()
print("IDFV:\(idfv)")
- ✅ 合規:Apple 官方提供的 API,無審核風險;
- ❌ 同一設備不同開發者的 App,IDFV 不同;
- ❌ 卸載該開發者所有 App 後,IDFV 會重新生成。
3. Advertising Identifier (IDFA)
swift
import AdSupport
import AppTrackingTransparency
/// 獲取IDFA(需用户授權)
func requestIDFA(completion: @escaping (String?) -> Void) {
// 1. 請求用户授權
ATTrackingManager.requestTrackingAuthorization { status in
DispatchQueue.main.async {
switch status {
case .authorized:
// 用户授權,獲取IDFA
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
completion(idfa)
case .denied, .restricted, .notDetermined:
// 未授權,返回nil
completion(nil)
@unknown default:
completion(nil)
}
}
}
}
// 使用示例
requestIDFA { idfa in
if let idfa = idfa {
print("IDFA:\(idfa)")
} else {
print("用户未授權獲取IDFA")
}
}
- ✅ 合規(需授權):必須向用户説明追蹤目的,且用户可隨時關閉;
- ❌ 僅適用於廣告相關場景,不可用於設備綁定;
- ❌ 用户可重置 IDFA,穩定性差。
三、特殊場景:開發 / 測試階段獲取 UDID
方式 1:通過 Xcode 獲取
方式 2:通過 iTunes/Finder 獲取
方式 3:通過第三方工具(如 TestFlight)
- 將設備添加到 TestFlight 測試組後,可在 TestFlight 後台查看設備 UDID。
四、關鍵注意事項
總結
- 設備綁定 / 用户統計:優先使用「UUID + Keychain」,穩定性最高;
- 同一開發者 App 標識:使用 IDFV;
- 廣告追蹤:使用 IDFA(需用户授權);