53

如何以编程方式访问 Bundle Seed ID/Team ID/App Identifier Prefix 字符串?(据我所知,这些都是一样的)。

我正在使用 UICKeychainStore 钥匙串包装器在多个应用程序中保存数据。这些应用程序中的每一个在其权利 plist 中都有一个共享的钥匙串访问组,并共享相同的配置文件。默认情况下,钥匙串服务使用 plist 中的第一个访问组作为保存数据的访问组。当我调试 UICKeychainStore 时,这看起来像“AS234SDG.com.myCompany.SpecificApp”。我想将访问组设置为“AS234SDG.com.myCompany.SharedStuff”,但我似乎无法找到如何以编程方式获取访问组的“AS234SDG”字符串,并且希望避免对其进行硬编码如果可能的话。

4

4 回答 4

101

Info.plist 可以有你自己的信息,如果你用 写一个值$(AppIdentifierPrefix),它会在构建阶段被替换为真正的应用标识符前缀。

所以,试试这个:

在您的 Info.plist 中,添加有关应用标识符前缀的信息。

<key>AppIdentifierPrefix</key>
<string>$(AppIdentifierPrefix)</string>

然后,您可以使用 Objective-C 以编程方式检索它:

NSString *appIdentifierPrefix =
    [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppIdentifierPrefix"];

和斯威夫特:

let appIdentifierPrefix =
    Bundle.main.infoDictionary!["AppIdentifierPrefix"] as! String

注意appIdentifierPrefix以句号结尾;例如AS234SDG.

于 2015-02-25T08:57:46.260 回答
58

您可以通过查看kSecAttrAccessGroup现有 KeyChain 项的访问组属性(即 )以编程方式检索捆绑种子 ID。在下面的代码中,我查找现有的 KeyChain 条目,如果不存在则创建一个。一旦我有一个 KeyChain 条目,我就会从中提取访问组信息,并返回访问组的第一个由“。”分隔的组件。(句点)作为捆绑种子 ID。

+ (NSString *)bundleSeedID {
    NSString *tempAccountName = @"bundleSeedID";
    NSDictionary *query = @{
        (__bridge NSString *)kSecClass : (__bridge NSString *)kSecClassGenericPassword,
        (__bridge NSString *)kSecAttrAccount : tempAccountName,
        (__bridge NSString *)kSecAttrService : @"",
        (__bridge NSString *)kSecReturnAttributes: (__bridge NSNumber *)kCFBooleanTrue,
    };
    CFDictionaryRef result = nil;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
    if (status == errSecItemNotFound)
        status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
    if (status != errSecSuccess) {
        return nil;
    }
    status = SecItemDelete((__bridge CFDictionaryRef)query); // remove temp item
    NSDictionary *dict = (__bridge_transfer NSDictionary *)result;
    NSString *accessGroup = dict[(__bridge NSString *)kSecAttrAccessGroup];
    NSArray *components = [accessGroup componentsSeparatedByString:@"."];
    NSString *bundleSeedID = [[components objectEnumerator] nextObject];
    return bundleSeedID;
}
于 2012-08-07T08:22:56.253 回答
9

这是@David H 答案的 Swift 版本:

static func bundleSeedID() -> String? {
        let queryLoad: [String: AnyObject] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: "bundleSeedID" as AnyObject,
            kSecAttrService as String: "" as AnyObject,
            kSecReturnAttributes as String: kCFBooleanTrue
        ]

        var result : AnyObject?
        var status = withUnsafeMutablePointer(to: &result) {
            SecItemCopyMatching(queryLoad as CFDictionary, UnsafeMutablePointer($0))
        }

        if status == errSecItemNotFound {
            status = withUnsafeMutablePointer(to: &result) {
                SecItemAdd(queryLoad as CFDictionary, UnsafeMutablePointer($0))
            }
        }

        if status == noErr {
            if let resultDict = result as? [String: Any], let accessGroup = resultDict[kSecAttrAccessGroup as String] as? String {
                let components = accessGroup.components(separatedBy: ".")
                return components.first
            }else {
                return nil
            }
        } else {
            print("Error getting bundleSeedID to Keychain")
            return nil
        }
    }
于 2017-10-13T09:49:26.887 回答
4

这是一个很好的问题,但要实现您想要做的事情,可能有一个不需要检索捆绑种子 ID 的解决方案。

从这篇文章中,关于您正在使用的相同钥匙串包装器:

默认情况下,它会在编写时选择您的 Entitlements.plist 中指定的第一个访问组,并在未指定任何访问组时搜索所有访问组。

然后将在授予访问权限的所有组中搜索密钥。因此,为了解决您的问题,您可以将所有捆绑应用程序的访问组添加到您的 entitlements.plist 中,而不是使用“共享的东西”组,将 $(CFBundleIdentifier) 作为您的第一个钥匙串组(然后您的钥匙串包装器将在此写入组),你都准备好了

于 2014-04-03T15:10:25.947 回答