【问题标题】:How to preserve identifierForVendor in ios after uninstalling ios app on device?在设备上卸载 ios 应用程序后如何在 ios 中保留 identifierForVendor?
【发布时间】:2014-03-19 16:04:24
【问题描述】:

我正在开发一个调用 Web 服务进行登录的 iOS 应用程序,当时我将登录凭据连同供应商标识符 (identifierForVendor) 一起发送到 Web 服务器,以便为这些凭据唯一标识设备。因此用户只能拥有一个设备和一个凭证。

我用

获得了 identifierForVendor
NSString *uuid = [[UIDevice currentDevice] identifierForVendor].UUIDString

然后此标识符将存储在 Web 服务器的数据库和设备数据库中。下次当用户打开应用程序并尝试从 Web 服务器下载数据时,用户设备上的本地 identifierForVendor 将与存储在 Web 服务器上的标识符进行比较。

当用户卸载应用程序并重新安装时出现问题,我发现 identifierForVendor 已更改。因此用户无法继续进行。

我阅读了苹果文档UIDevice Documentation

如上所述,如果来自同一供应商的所有应用程序都从设备上卸载,那么在新安装该供应商的任何应用程序时,将采用新的标识符 ForVendor。

那么在我的情况下如何处理呢?

【问题讨论】:

  • 我不知道是否可以,但是将它保存在 Keychain 中怎么样?您在启动时检查此标识符是否在 KeyChain 中,如果没有,则获取一个并将其存储在 Keychain 中。
  • 您好 Gekb,您是否为您的查询找到任何解决方案。就连我也面临同样的情况。

标签: ios uuid uniqueidentifier identifier


【解决方案1】:

您可以将其保存在 KeyChain 中

-(NSString *)getUniqueDeviceIdentifierAsString
{

 NSString *appName=[[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleNameKey];

 NSString *strApplicationUUID = [SSKeychain passwordForService:appName account:@"incoding"];
 if (strApplicationUUID == nil)
 {
    strApplicationUUID  = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    [SSKeychain setPassword:strApplicationUUID forService:appName account:@"incoding"];
 }

 return strApplicationUUID;
}

【讨论】:

  • 如果启用了 iCloud KeyChain 同步,这会起作用吗?
  • 有趣的问题。不知道
  • 这在 KeyChain 同步时不起作用,所有同步的设备将获得相同的 verdor id。
  • 由于上面提到的同步问题,我将投反对票。
  • 请注意钥匙串,钥匙串中的项目目前可以在应用程序卸载-安装周期中存活,但将来可能会改变。在 iOS 10.3 beta 3 中,钥匙串项目被删除,但在最终版本中又改回来了。在stackoverflow.com/questions/18911434/… 上查看更多信息。
【解决方案2】:

一般情况下,不要使用identifierForVendor。相反,请使用NSUUID 生成自定义 UUID 并将其存储在钥匙串中(因为如果删除并重新安装应用程序,钥匙串不会被删除)。

【讨论】:

  • 在不同设备上同一应用的不同版本的情况下,identifierForVendor 是否有可能返回重复项?
  • 我希望是的,但机会非常小
  • 没有重复的机会。 UUID 不是随机的。它们是计算出来的,UUID 的一部分是设备 ID。
  • "由 NSUUID 创建的 UUID 符合 RFC 4122 第 4 版,并使用随机字节创建。" - 但是重复的可能性非常小,正如其他人发布的那样“只有在接下来的 100 年每秒生成 10 亿个 UUID 之后,仅创建一个重复的概率约为 50%。” - stackoverflow.com/a/1155027/859027
  • 使用 identifierForVendor 很好,但不应再将其存储在钥匙串中。 stackoverflow.com/a/48405739/62921 iOS 10.3 后不再持久
【解决方案3】:

添加到@nerowolfe 的answer

SSKeychain 使用kSecAttrSynchronizableAny 作为默认同步模式。您可能不希望 identifierForVendor 在多个设备之间同步,所以这里有一个代码:

// save identifierForVendor in keychain without sync
NSError *error = nil;
SSKeychainQuery *query = [[SSKeychainQuery alloc] init];
query.service = @"your_service";
query.account = @"your_account";
query.password = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
query.synchronizationMode = SSKeychainQuerySynchronizationModeNo;
[query save:&error];

【讨论】:

  • 来自 Cocoapods:SSKeychain 已被弃用,取而代之的是 SAMKeychain
【解决方案4】:

您可以尝试使用 KeyChain 来保存您的 VendorIdentifier,即使您卸载了您的应用程序,它也会在您的设备重置之前一直存在。

【讨论】:

  • 不幸的是,他们最近更改了它,KeyChain 不再安全地存储持久数据。 stackoverflow.com/a/48405739/62921
  • @ForceMagic 感谢更新,我的答案是 3 岁,会找到另一种方法,所有问题的答案仍然存储在 Keychain 中,它们没有更新,投票没有意义,因为它曾经提出问题时工作..
  • 是的,我明白几乎所有的答案都是旧的。我看到的反对票更像是“这不再是真的”,而不是“这是一个糟糕的答案”。但也许我错了。
  • @ForceMagic 答案仍然正确,请参阅stackoverflow.com/a/18944600/790842stackoverflow.com/a/43063683/790842
  • 来自您的链接:At Apple Documentation It is suggested that this is about to change and we should NOT rely on keychain access data being intact after an app uninstallation
【解决方案5】:

好的。我不想使用第三方——即 SSKeychain。所以这是我尝试过的代码,相当简单并且效果很好:

    NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:bundleId accessGroup:nil];
if(![keychainItem objectForKey:(__bridge id)(kSecValueData)]){
    NSString *idfa = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    [keychainItem setObject:idfa forKey:(__bridge id)(kSecValueData)];
    NSLog(@"saving item %@", [keychainItem objectForKey:(__bridge id)(kSecValueData)]);
}else{
    NSLog(@"saved item is %@", [keychainItem objectForKey:(__bridge id)(kSecValueData)]);
}

【讨论】:

  • +1 这是一个非常简单的解决方案,结合 iCloud 钥匙串同步(使用属性 kSecAttrSynchronizable),是这个问题的最佳解决方案。
  • @loretoparisi 如果您使用 kSecAttrSynchronizable,您在所有设备上的价值不都相同吗?
  • @jcesarmobile 确切地说,如果您将kSecAttrSynchronizable 添加到@gautam-jain 代码,您将在 iCloud 应用程序属性中获得它并同步到您的所有设备。
  • 那么如果您希望每个设备具有不同的值,这不是一个好主意
  • @jcesarmobile 是的,根据您的需要,您可以保留设备的标识符,以及来自ASIdentifierManager 的类似属性identifierForVendor advertisingIdentifier 的设备供应商。
【解决方案6】:

斯威夫特版本

func UUID() -> String {

    let bundleName = NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String
    let accountName = "incoding"

    var applicationUUID = SAMKeychain.passwordForService(bundleName, account: accountName)

    if applicationUUID == nil {

        applicationUUID = UIDevice.currentDevice().identifierForVendor!.UUIDString

        // Save applicationUUID in keychain without synchronization
        let query = SAMKeychainQuery()
        query.service = bundleName
        query.account = accountName
        query.password = applicationUUID
        query.synchronizationMode = SAMKeychainQuerySynchronizationMode.No

        do {
            try query.save()
        } catch let error as NSError {
            print("SAMKeychainQuery Exception: \(error)")
        }
    }

    return applicationUUID
}

【讨论】:

  • 我不会称之为 Swift 版本,因为它需要一个 3rd 方对象
【解决方案7】:

再也没有明确的方法可以将唯一号码链接到设备,Apple 隐私准则不允许这样做。

您可以尝试将自己的唯一 ID 保存在钥匙串中,但如果用户清除他的设备,此 ID 也会消失。

通常link a device to a user 是错误的,因为您不再识别用户而是识别设备。因此,您应该只更改您的 API,以便用户可以重新登录,并且将供应商 ID 绑定到用户帐户。

此外,当用户拥有不止一台设备(例如 iPhone 和 iPad)并在这两种设备上都使用您的应用时,会发生什么情况?由于您的身份验证基于唯一 ID,因此无法完成。

【讨论】:

  • @DouglasHeld 我明白这一点,但 Apple 让这成为不可能,我试图解释为什么你不应该这样做。
  • @rckoenes 如果需要硬件绑定,则需要识别设备。您还能如何阻止用户在多个设备上安装它?一个人支付一次,然后他所有的朋友都可以使用他的登录凭据免费获得它!?对开发人员来说似乎不合理。
  • @ash 我听到了,但 Apple 不允许这样做。支持每个 Apple 帐户 5 个设备。限制这一点违反了 Apple 政策。
【解决方案8】:

我曾使用KeychainAccess pod 来解决这个问题。

在你的 pod 文件中:

pod 'KeychainAccess', '~> 2.4' //If you are using Swift 2.3 
pod 'KeychainAccess' //Defaults to 3.0.1 which is in Swift 3

在要在钥匙串中设置 UUID 的文件中导入 KeychainAccess 模块

import KeychainAccess

使用以下代码从钥匙串中设置和获取 UUID:

注意: BundleId 是键,UUID 是值

var bundleID = NSBundle.mainBundle().bundleIdentifier
    var uuidValue = UIDevice.currentDevice().identifierForVendor!.UUIDString

 //MARK: - setVenderId and getVenderId
    func setVenderId() {

        let keychain = Keychain(service: bundleID!)

        do {
            try keychain.set(venderId as String, key: bundleID!)
            print("venderId set : key \(bundleID) and value: \(venderId)")
        }
        catch let error {
            print("Could not save data in Keychain : \(error)")
        }
    }

    func getVenderId() -> String {
        let keychain = Keychain(service: bundleID!)
        let token : String = try! keychain.get(bundleID!)!
        return token
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-30
    • 2016-06-16
    • 2013-12-18
    • 2014-03-27
    • 2020-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多