我了解我可以通过启用应用群组和使用NSUserDefaults在我的共享扩展程序及其包含的应用之间共享数据(请参阅Sharing data between an iOS 8 share extension and main app)。
但是,我存储的数据很敏感,所以我希望使用钥匙串。因此,用户可以在包含应用程序中输入帐户信息,然后共享扩展程序将读取该数据以执行预期的共享操作。
有人知道这是否可行?我的第一个破解建议扩展和包含应用程序具有单独的钥匙串(在包含应用程序中使用密钥保存数据时,在尝试返回扩展中该键的数据时返回null)。
谢谢!
P.S。使用Lockbox进行Keychain访问,但是如果它太过抽象而无法使其工作,我可能会抛弃它。 https://github.com/granoff/Lockbox
答案 0 :(得分:11)
使密钥链在Xcode 8中共享。
1)在您的应用程序目标中找到并启用“Keychain Sharing”,添加一个Keychain Group键(一个反向域样式的字符串,如com.myappdomain.myappname)
2)对扩展目标执行完全相同的操作。确保钥匙串组密钥对于应用程序和扩展程序都是相同的。
以常规方式从Keychain添加和检索数据,代码中无需进行任何特殊更改。例如,这里是我如何将数据放入主应用程序中的Keychain(有点过时但仍然适用于Swift 3):
let login = loginString
let domain = domainString
let passwordData: Data = passwordString.data(using: String.Encoding.utf8, allowLossyConversion: false)!
let keychainQuery: [NSString: NSObject] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: login as NSObject, // login and domain strings help identify
kSecAttrService: domain as NSObject, // the required record in the Keychain
kSecValueData: passwordData as NSObject]
SecItemDelete(keychainQuery as CFDictionary) //Deletes the item just in case it already exists
let keychainSaveStatus: OSStatus = SecItemAdd(keychainQuery as CFDictionary, nil)
然后在扩展程序中检索它:
let keychainQuery: [NSString: NSObject] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: login as NSObject,
kSecAttrService: domain as NSObject,
kSecReturnData: kCFBooleanTrue,
kSecMatchLimit: kSecMatchLimitOne]
var rawResult: AnyObject?
let keychain_get_status: OSStatus = SecItemCopyMatching(keychainQuery as CFDictionary, &rawResult)
if (keychain_get_status == errSecSuccess) {
if let retrievedData = rawResult as? Data,
let password = String(data: retrievedData, encoding: String.Encoding.utf8) {
// "password" contains the password string now
}
}
请注意,您仍需要将“login”和“domain”传递给扩展程序,以便识别正确的记录。这可以通过NSUserDefaults完成。有关如何执行此操作,请参阅this answer。
答案 1 :(得分:9)
这可以做到。它是创建一个框架来进行Keychain访问,并在“Capabilities”下启用“Activate Keychain Sharing”的组合。这个链接告诉我我需要知道的事情:http://swiftandpainless.com/ios8-share-extension-with-a-shared-keychain/
答案 2 :(得分:0)
在桥接标题中使用标准的Objective-C KeychainItemWrapper类和#import“KeychainItemWrapper.h”条目:
func btnSaveAction() {
let appGroupID = "group.com.yourcompany.appid"
let keychain = KeychainItemWrapper(identifier: "Password", accessGroup:appGroupID)
keychain.setObject(self.txtfldPassword.text!, forKey:kSecValueData)
keychain.setObject(self.txtfldEmail.text!, forKey:kSecAttrAccount)
}
在Watch扩展端(Swift):
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
let appGroupID = "group.com.yourcompany.appid"
let keychain = KeychainItemWrapper(identifier: "Password", accessGroup:appGroupID)
println(keychain.objectForKey(kSecAttrAccount))
println(keychain.objectForKey(kSecValueData))
}
在Objective C中,watchkit扩展:
NSString *appGroupID = @"group.com.yourcompany.appid";
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"Password" accessGroup:appGroupID];
[keychain setObject:(__bridge id)(kSecAttrAccessibleWhenUnlocked) forKey:(__bridge id)(kSecAttrAccessible)];
NSLog(@"account = %@", [keychain objectForKey:(__bridge id)(kSecAttrAccount)]);
NSLog(@"password =%@", [keychain objectForKey:(__bridge id)(kSecValueData)]);
不要忘记为同一个钥匙串组的手机应用程序和手表套件扩展程序启用“功能”下的“钥匙串共享”:“group.com.yourcompany.appid”
答案 3 :(得分:0)
我为应用程序和共享扩展的目标启用了应用程序组功能。 然后我只是添加了属性:
kSecAttrAccessGroup: "group.lolrandomname",
而且它工作正常:我可以从应用程序和我的共享扩展访问相同的存储项目:)
答案 4 :(得分:-1)
使用以下链接中的KeychainItemWrapper类,并将您的组标识符作为accessgroup传递。