长期以来,我一直使用以下代码。我认为这是独一无二的
但是我已经删除了我的应用并重新安装了它,我得到了新的不同的设备ID。
if let uuid = UIDevice.current.identifierForVendor?.uuidString {
print(uuid)
}
每次重新安装时,我都会获得一个新ID。
如何获得保持不变的东西?
答案 0 :(得分:3)
由于在删除应用程序时可以清除从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
}
这是用于从钥匙串存储/检索的类:
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
}
}
然后,您只需拥有一个用户类,即可在其中获得标识符:
let uuid = getUUID()
print("UUID: \(uuid)")
如果将其放在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
}
}
请注意,要使用MD5功能,您必须将以下导入添加到应用程序中的Objective-C桥接标头中:(,如果要使用Xcode <10构建。在Xcode 10+中包含CommonCrypto因此您可以跳过此步骤)
#import <CommonCrypto/CommonCrypto.h>
如果您的应用程序没有桥接头,则将其添加到项目中,并确保在构建设置中对其进行设置:
设置完成后,您可以像这样生成自己的自定义uuid:
let otherUuid = UUID.custom()
print("Other: \(otherUuid)")
运行应用程序并记录两个输出都会生成如下的uuid:
// uuid from first example
UUID: Optional("8A2496F0-EFD0-4723-8C6D-8E18431A49D2")
// uuid from second custom example
Other: Optional("63674d91f08ec3aaa710f3448dd87818")
答案 1 :(得分:2)
现在已禁止访问唯一设备ID(UDID)。 identifierForVendor
是它的替代品,并且它的行为始终都有记录。
答案 2 :(得分:0)
iPhone中的唯一ID是UDID,在当前版本的OS中无法访问,因为它可能被滥用。因此,Apple为唯一密钥提供了另一个选项,但是每次您安装该应用程序时它都会更改。 --Cannot access UDID
但是还有另一种实现此功能的方法。
首先,您必须生成唯一ID:
func createUniqueID() -> String {
let uuid: CFUUID = CFUUIDCreate(nil)
let cfStr: CFString = CFUUIDCreateString(nil, uuid)
let swiftString: String = cfStr as String
return swiftString
}
获取到唯一的内容后,但在应用安装并重新安装后会发生变化。 将该ID保存到任何说“ uniqueID”的键上的键链中。
要将密钥保存在keyChain中:
func getDataFromKeyChainFunction() {
let uniqueID = KeyChain.createUniqueID()
let data = uniqueID.data(using: String.Encoding.utf8)
let status = KeyChain.save(key: "uniqueID", data: data!)
if let udid = KeyChain.load(key: "uniqueID") {
let uniqueID = String(data: udid, encoding: String.Encoding.utf8)
print(uniqueID!)
}
}
func save(key: String, data: Data) -> OSStatus {
let query = [
kSecClass as String : kSecClassGenericPassword as String,
kSecAttrAccount as String : key,
kSecValueData as String : data ] as [String : Any]
SecItemDelete(query as CFDictionary)
return SecItemAdd(query as CFDictionary, nil)
}
接下来,当您需要基于uniqueID执行任何任务时,请首先检查是否有任何数据保存在键“ uniqueID”上的键链中。 即使您卸载了该应用程序,钥匙串数据仍然保留,但仍将被操作系统删除。
func checkUniqueID() {
if let udid = KeyChain.load(key: "uniqueID") {
let uniqueID = String(data: udid, encoding: String.Encoding.utf8)
print(uniqueID!)
} else {
let uniqueID = KeyChain.createUniqueID()
let data = uniqueID.data(using: String.Encoding.utf8)
let status = KeyChain.save(key: "uniqueID", data: data!)
print("status: ", status)
}
}
通过这种方式,您可以一次生成唯一ID,然后每次使用此ID。
注意:但是,当您上传应用程序的下一版本时,请使用相同的配置文件上传它,否则您将无法访问上次安装的应用程序的钥匙串存储。 密钥链存储与预配置文件关联。