一、核心前提:Apple 已禁用直接獲取 UDID

首先要明確:從 iOS 5 開始,Apple 就禁止開發者直接獲取設備的硬件 UDID(如uniqueIdentifier屬性),該 API 已被廢棄,使用它的 App 會被 App Store 拒絕。

Apple 禁用的核心原因是用户隱私保護 —— 硬件 UDID 是設備的唯一標識,可能被用於追蹤用户,違反隱私政策。


二、合規的 UDID 替代方案(推薦)

雖然無法獲取硬件 UDID,但可以通過以下合規方式生成 / 獲取設備唯一標識,滿足大多數業務場景(如設備綁定、用户統計):

1. UUID + Keychain 存儲(最常用)

核心思路

  • 首次啓動 App 時生成一個隨機的 UUID(通用唯一識別碼);
  • 將 UUID 存儲到 Keychain(鑰匙串)中(Keychain 在 App 卸載後仍保留,重裝 App 可恢復);
  • 後續每次啓動 App,先從 Keychain 讀取,無則重新生成。

實現代碼(Swift)

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)

核心思路:獲取同一開發者賬號下所有 App 的設備唯一標識(同一設備、同一開發者的 App,IDFV 相同;卸載該開發者所有 App 後,IDFV 會重置)。

實現代碼(Swift)

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)

核心思路:廣告標識,用於廣告追蹤,用户可在「設置 - 隱私 - 廣告」中重置 / 關閉(需添加權限描述)。

實現步驟

  1. 添加依賴:項目中導入AdSupport框架;
  2. 添加隱私描述:在Info.plist中添加NSUserTrackingUsageDescription(説明追蹤目的,否則會崩潰);
  3. 代碼實現:

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

如果你是開發者,需要在測試階段獲取設備的硬件 UDID(用於添加測試設備到開發者賬號),可通過以下方式:

方式 1:通過 Xcode 獲取

  1. 將設備連接到 Mac;
  2. 打開 Xcode → 點擊「Window」→ 「Devices and Simulators」;
  3. 選中連接的設備,即可看到「Identifier」(即 UDID),點擊右側複製按鈕即可。

方式 2:通過 iTunes/Finder 獲取

  1. 連接設備到 Mac;
  2. macOS Catalina 及以上:打開 Finder → 選中設備 → 點擊「通用」→ 點擊「序列號」,會切換為 UDID,右鍵複製;
  3. 舊版 macOS:打開 iTunes → 選中設備 → 點擊「摘要」→ 點擊「序列號」,切換為 UDID 後複製。

方式 3:通過第三方工具(如 TestFlight)

  • 將設備添加到 TestFlight 測試組後,可在 TestFlight 後台查看設備 UDID。

四、關鍵注意事項

  1. 禁止使用私有 API:不要嘗試通過私有框架(如IOKit)獲取硬件 UDID,會導致 App 被拒,且 iOS 系統會嚴格限制私有 API 調用;
  2. 隱私政策説明:如果使用 IDFA/UUID 追蹤用户,必須在 App 的隱私政策中明確説明,否則會違反 App Store 審核規則;
  3. 數據安全:存儲 UUID/IDFV 時,避免明文傳輸到服務器(可加密後傳輸),符合數據合規要求。

總結

  1. 核心結論:無法通過代碼獲取 iOS 設備的硬件 UDID(Apple 已禁用),需使用合規替代方案;
  2. 推薦方案
  • 設備綁定 / 用户統計:優先使用「UUID + Keychain」,穩定性最高;
  • 同一開發者 App 標識:使用 IDFV;
  • 廣告追蹤:使用 IDFA(需用户授權);
  1. 開發測試:獲取硬件 UDID 僅用於添加測試設備,可通過 Xcode/Finder 實現,無需代碼。

所有方案均遵循 Apple 隱私政策,可安全用於生產環境,避免因違規獲取 UDID 導致 App 審核被拒。