Как получить действительно фиксированный Device-ID в swift?
Я использую приведенный ниже код с давних времен. Я думал, что это уникально
Но я удалил свое приложение и переустановил его, я получаю новый другой Device-ID!!!
if let uuid = UIDevice.current.identifierForVendor?.uuidString {
print(uuid)
}
при каждой новой переустановке я получаю новый ID!
Я хочу что-то, что остается прежним.
2 ответа
Поскольку значение возвращается из 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)")
Запуск приложения и регистрация обоих выходных данных генерирует uuids что-то вроде этого:
// uuid from first example
UUID: Optional("8A2496F0-EFD0-4723-8C6D-8E18431A49D2")
// uuid from second custom example
Other: Optional("63674d91f08ec3aaa710f3448dd87818")
Уникальный идентификатор в iPhone - это UDID, который недоступен в текущей версии ОС, поскольку его можно использовать не по назначению. Поэтому Apple предоставила другой вариант уникального ключа, но он меняется каждый раз, когда вы устанавливаете приложение. - Не может получить доступ к UDID
Но есть и другой способ реализовать эту функцию.
Сначала вы должны сгенерировать уникальный идентификатор:
func createUniqueID() -> String {
let uuid: CFUUID = CFUUIDCreate(nil)
let cfStr: CFString = CFUUIDCreateString(nil, uuid)
let swiftString: String = cfStr as String
return swiftString
}
После получения этого, который является уникальным, но меняется после установки и переустановки приложения. Сохраните этот идентификатор в Key-Chain на любом ключе, скажем, "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)
}
Затем, когда вам необходимо выполнить какое-либо задание на основе уникального идентификатора, сначала проверьте, сохранены ли какие-либо данные в цепочке для ключей по ключу "уникальный идентификатор". Даже если вы удалите приложение, данные цепочки ключей сохранятся, они будут удалены операционной системой.
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)
}
}
Таким образом, вы можете сгенерировать уникальный идентификатор один раз и использовать этот идентификатор каждый раз.
ПРИМЕЧАНИЕ. Но при загрузке следующей версии приложения загрузите ее с тем же профилем обеспечения, в противном случае вы не сможете получить доступ к хранилищу ключей последней установленной программы. Key-Chain Store связан с профилем Provisioning.
Доступ к уникальному идентификатору устройства (UDID) уже давно запрещен. identifierForVendor
это его замена, и его поведение всегда было задокументировано.