Доля между расширением iOS и содержащим приложение с брелка?

Я понимаю, что я могу обмениваться данными между моим расширением share и его содержащим приложением, включив группы приложений и используя NSUserDefaults (см. Обмен данными между расширением общего доступа iOS 8 и основным приложением).

Однако данные, которые я храню, чувствительны, поэтому я надеялся использовать брелок. Таким образом, пользователь будет вводить информацию об учетной записи в содержащем приложении, а затем расширение общего доступа будет читать эти данные для выполнения запланированного действия совместного использования.

Кто-нибудь знает, возможно ли это? Моя первая трещина в нем предполагает, что расширение и содержащее приложение имеют отдельные брелки (сохранение данных с помощью ключа в содержащем приложении возвращает значение null при попытке вернуть данные для этого ключа в расширении).

Спасибо!

P.S. Использование Lockbox для доступа к Keychain, но я мог бы отбросить его, если это слишком большая абстракция, чтобы заставить его работать. https://github.com/granoff/Lockbox

Ответ 1

Это можно сделать. Это комбинация создания рамки для доступа к Keychain и включения "Активировать совместное использование ключей" в разделе "Возможности". Эта ссылка рассказала мне, что мне нужно знать: http://swiftandpainless.com/ios8-share-extension-with-a-shared-keychain/

Ответ 2

Чтобы связать брелок в Xcode 8.

1) В целевом приложении App в Capabilities найдите и включите "Совместное использование ключей", добавьте ключ Keychain Group (строка стиля обратного домена, такая как com.myappdomain.myappname)

2) Сделайте то же самое для цели расширения. Убедитесь, что клавиша Keychain Group одинакова для обоих - приложения и расширения.

Добавлять и извлекать данные из 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(keychainQueryForPass 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. См. этот ответ о том, как это сделать.

Ответ 3

Используя стандартный класс 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"