如何快速获取真正的固定Device-ID? [英] How to get the realy fixed Device-ID in swift?

查看:174
本文介绍了如何快速获取真正的固定Device-ID?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I use the below code since long time. I have thought it is unique

但是我已经删除了我的应用并重新安装了它,我得到了新的不同的设备ID.

But I have deleted my app and reinstalled it, I get new different Device-ID.

if let uuid = UIDevice.current.identifierForVendor?.uuidString {
  print(uuid)
}

每次重新安装时,我都会得到一个新ID.

every new reinstall, I get a new ID.

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

How do I get something which stays the same?

推荐答案

由于删除应用程序时可以清除从identifierForVendor返回的值,或者如果用户在设置"应用程序中将其重置,则可以重置该值,因此必须管理持久性你自己.

Since the value returned from identifierForVendor can be cleared when deleting the app or reset if the user resets it in the Settings app, you have to manage persisting it yourself.

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

There are a few ways to accomplish this. You can setup a server that assigns a uuid which is then persisted and fetched server side via user login, or you can create and store it locally in the keychain.

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

Items stored in the keychain will not be deleted when the app is deleted. This allows you to check if a uuid was previously stored, if so you can retrieve it, if not you can generate a new uuid and persist it.

这是您可以在本地进行的一种方式:

Here's a way you could do it locally:

/// 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
}

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

And here's the class for storing/retrieving from the keychain:

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
    }
}

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

Then you can just have a user class where you get the identifier:

let uuid = getUUID()
print("UUID: \(uuid)") 

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

If you put this in a test app in viewDidLoad, launch the app and note the uuid printed in the console, delete the app and relaunch and you'll have the same uuid.

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

You can also create your own completely custom uuid in the app if you like by doing something like this:

// 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
    }
}

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

Note that to use the MD5 function you will have to add the following import to an Objective-C bridging header in your app: (if you're building with Xcode < 10. In Xcode 10+ CommonCrypto is included so you can skip this step)

#import <CommonCrypto/CommonCrypto.h>

如果您的应用程序没有桥接头,则将其添加到项目中,并确保在构建设置中对其进行设置:

If your app does not have a bridging header, add one to your project and make sure to set it in build settings:

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

Once it's setup you can generate your own custom uuid like this:

let otherUuid = UUID.custom()
print("Other: \(otherUuid)")

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

Running the app and logging both outputs generates uuids something like this:

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

// uuid from second custom example
Other: Optional("63674d91f08ec3aaa710f3448dd87818")

这篇关于如何快速获取真正的固定Device-ID?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆