アプリグループを有効にし、NSUserDefaultsを使用することで、共有拡張機能とそれに含まれるアプリの間でデータを共有できることを理解しています( iOS 8共有拡張機能とメインアプリ間のデータ共有 を参照)。
ただし、保存しているデータは機密性が高いため、キーチェーンを使用したいと思いました。そのため、ユーザーは含まれているアプリにアカウント情報を入力し、共有拡張機能はそのデータを読み取って目的の共有アクションを実行します。
これが可能かどうか誰かが知っていますか?それに対する私の最初の亀裂は、拡張機能とそれを含むアプリが別々のキーチェーンを持っていることを示唆しています(拡張機能でそのキーのデータを返そうとすると、包含アプリのキーでデータを保存するとnullが返されます)。
ありがとう!
P.S.キーチェーンアクセスにロックボックスを使用しますが、抽象化が多すぎて機能しない場合は、ロックボックスを破棄することができます。 https://github.com/granoff/Lockbox
キーチェーンをXcode8で共有するため。
1)機能のアプリターゲットで「キーチェーン共有」を見つけてオンにし、キーチェーングループキー(com.myappdomain.myappnameのようなリバースドメインスタイルの文字列)を追加します
2)拡張ターゲットについてもまったく同じことを行います。キーチェーングループキーが、アプリと拡張機能の両方で同じであることを確認してください。
通常の方法でキーチェーンにデータを追加および取得します。コードに特別な変更を加える必要はありません。たとえば、メインアプリのキーチェーンにデータを配置する方法は次のとおりです(少し古風ですが、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
}
}
正しいレコードを識別するために、「ログイン」と「ドメイン」を拡張機能に渡す必要があることに注意してください。これは、NSUserDefaultsを介して実行できます。これを行う方法については、 この回答 を参照してください。
これは行うことができます。これは、キーチェーンアクセスを行うためのフレームワークを作成することと、[機能]の下の[キーチェーン共有をアクティブ化する]をオンにすることの組み合わせです。このリンクは私が知る必要があることを教えてくれました: http://swiftandpainless.com/ios8-share-extension-with-a-shared-keychain/
標準のObjective-CKeychainItemWrapperクラスを使用し、ブリッジヘッダーに#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)
}
ウォッチ拡張側(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では、ウォッチキットの拡張機能は次のとおりです。
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」の電話アプリと時計キット拡張機能の両方で、「機能」の下にある「キーチェーン共有」をオンにすることを忘れないでください。