如何快速获取真正的固定Device-ID?

use*_*085 1 swift deviceid

长期以来,我一直使用以下代码。我认为这是独一无二的

但是我删除了我的应用程序并重新安装了它,我得到了新的不同的设备ID。

if let uuid = UIDevice.current.identifierForVendor?.uuidString {
  print(uuid)
}
Run Code Online (Sandbox Code Playgroud)

每次重新安装,我都会得到一个新的ID。

我如何获得保持不变的东西?

dig*_*und 7

由于从返回的值identifierForVendor可以在删除应用程序时清除,也可以在用户在“设置”应用程序中将其重置时重置,因此您必须自行管理持久性。

有几种方法可以完成此操作。您可以设置一个服务器,该服务器分配一个uuid,然后通过用户登录将其保存并提取到服务器端,也可以在本地创建并将其存储在钥匙串中。

删除应用程序后,不会删除钥匙串中存储的项目。这使您可以检查以前是否存储过uuid,如果可以,则可以检索它,否则可以生成一个新的uuid并将其持久化。

您可以在本地进行以下操作:

/// Creates a new unique user identifier or retrieves the last one created
func getUUID() -> String? {

    // create a keychain helper instance
    let keychain = KeychainAccess()

    // this is the key we'll use to store the uuid in the keychain
    let uuidKey = "com.myorg.myappid.unique_uuid"

    // check if we already have a uuid stored, if so return it
    if let uuid = try? keychain.queryKeychainData(itemKey: uuidKey), uuid != nil {
        return uuid
    }

    // generate a new id
    guard let newId = UIDevice.current.identifierForVendor?.uuidString else {
        return nil
    }

    // store new identifier in keychain
    try? keychain.addKeychainData(itemKey: uuidKey, itemValue: newId)

    // return new id
    return newId
}
Run Code Online (Sandbox Code Playgroud)

这是用于从钥匙串存储/检索的类:

import Foundation

class KeychainAccess {

    func addKeychainData(itemKey: String, itemValue: String) throws {
        guard let valueData = itemValue.data(using: .utf8) else {
            print("Keychain: Unable to store data, invalid input - key: \(itemKey), value: \(itemValue)")
            return
        }

        //delete old value if stored first
        do {
            try deleteKeychainData(itemKey: itemKey)
        } catch {
            print("Keychain: nothing to delete...")
        }

        let queryAdd: [String: AnyObject] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: itemKey as AnyObject,
            kSecValueData as String: valueData as AnyObject,
            kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
        ]
        let resultCode: OSStatus = SecItemAdd(queryAdd as CFDictionary, nil)

        if resultCode != 0 {
            print("Keychain: value not added - Error: \(resultCode)")
        } else {
            print("Keychain: value added successfully")
        }
    }

    func deleteKeychainData(itemKey: String) throws {
        let queryDelete: [String: AnyObject] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: itemKey as AnyObject
        ]

        let resultCodeDelete = SecItemDelete(queryDelete as CFDictionary)

        if resultCodeDelete != 0 {
            print("Keychain: unable to delete from keychain: \(resultCodeDelete)")
        } else {
            print("Keychain: successfully deleted item")
        }
    }

    func queryKeychainData (itemKey: String) throws -> String? {
        let queryLoad: [String: AnyObject] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: itemKey as AnyObject,
            kSecReturnData as String: kCFBooleanTrue,
            kSecMatchLimit as String: kSecMatchLimitOne
        ]
        var result: AnyObject?
        let resultCodeLoad = withUnsafeMutablePointer(to: &result) {
            SecItemCopyMatching(queryLoad as CFDictionary, UnsafeMutablePointer($0))
        }

        if resultCodeLoad != 0 {
            print("Keychain: unable to load data - \(resultCodeLoad)")
            return nil
        }

        guard let resultVal = result as? NSData, let keyValue = NSString(data: resultVal as Data, encoding: String.Encoding.utf8.rawValue) as String? else {
            print("Keychain: error parsing keychain result - \(resultCodeLoad)")
            return nil
        }
        return keyValue
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您只需拥有一个用户类,即可在其中获得标识符:

let uuid = getUUID()
print("UUID: \(uuid)") 
Run Code Online (Sandbox Code Playgroud)

如果将它放在viewDidLoad中的测试应用程序中,请启动该应用程序并注意控制台中打印的uuid,删除该应用程序并重新启动,您将拥有相同的uuid。

如果愿意,您还可以在应用程序中创建自己的完全自定义的uuid,方法如下:

// convenience extension for creating an MD5 hash from a string
extension String {

    func MD5() -> Data? {
        guard let messageData = data(using: .utf8) else { return nil }

        var digestData = Data(count: Int(CC_MD5_DIGEST_LENGTH))
        _ = digestData.withUnsafeMutableBytes { digestBytes in
            messageData.withUnsafeBytes { messageBytes in
                CC_MD5(messageBytes, CC_LONG(messageData.count), digestBytes)
            }
        }

        return digestData
    }
}

// extension on UUID to generate your own custom UUID
extension UUID {

    static func custom() -> String? {
        guard let bundleID = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String else {
            return nil
        }

        let unique = bundleID + NSUUID().uuidString
        let hashData = unique.MD5()
        let md5String = hashData?.map { String(format: "%02hhx", $0) }.joined()

        return md5String
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,要使用MD5功能,您必须将以下导入添加到应用程序中的Objective-C桥接标头中:(如果您使用Xcode <10构建。在Xcode 10+中包含CommonCrypto,因此您可以跳过此步骤

#import <CommonCrypto/CommonCrypto.h>
Run Code Online (Sandbox Code Playgroud)

如果您的应用程序没有桥接头,请在您的项目中添加一个头,并确保在构建设置中进行设置:

屏幕截图

设置完成后,您可以生成自己的自定义uuid,如下所示:

let otherUuid = UUID.custom()
print("Other: \(otherUuid)")
Run Code Online (Sandbox Code Playgroud)

运行应用程序并记录两个输出都会生成如下的uuid:

// uuid from first example
UUID: Optional("8A2496F0-EFD0-4723-8C6D-8E18431A49D2")

// uuid from second custom example
Other: Optional("63674d91f08ec3aaa710f3448dd87818")
Run Code Online (Sandbox Code Playgroud)


Ger*_*eon 1

多年来一直不允许访问唯一设备 ID (UDID)。identifierForVendor是它的替代品,并且它的行为始终被记录下来。