web-dev-qa-db-ja.com

Swift)を使用してキーチェーンにアイテムを追加する

Swiftを使用してiOSキーチェーンにアイテムを追加しようとしていますが、キャストを正しく入力する方法がわかりません。WWDC2013セッション709から、次のObjective-Cコードを指定します。

NSData *secret = [@"top secret" dataWithEncoding:NSUTF8StringEncoding];
NSDictionary *query = @{
    (id)kSecClass: (id)kSecClassGenericPassword,
    (id)kSecAttrService: @"myservice",
    (id)kSecAttrAccount: @"account name here",
    (id)kSecValueData: secret,
};

OSStatus = SecItemAdd((CFDictionaryRef)query, NULL);

次のようにSwiftでそれを実行しようとしています:

var secret: NSData = "Top Secret".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
var query: NSDictionary = [
    kSecClass: kSecClassGenericPassword,
    kSecAttrService: "MyService",
    kSecAttrAccount: "Some account",
    kSecValueData: secret
]

「式のタイプ「Dictionary」を「DictionaryLiteralConvertible」に変換できません」というエラーが発生します。

私が取った別のアプローチは、Swiftと- setObject:forKey:キーkSecClassを使用してkSecClassGenericPasswordを追加する辞書のメソッド。

Objective-Cの場合:

NSMutableDictionary *searchDictionary = [NSMutableDictionary dictionary];
[searchDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];

Objective-Cコードでは、さまざまなキーチェーンアイテムクラスキーのCFTypeRefがidを使用してブリッジされます。 Swift documentation )SwiftはidをAnyObjectとしてインポートすると記載されています。しかし、kSecClassをメソッドのAnyObjectとしてダウンキャストしようとしたとき、「Type'AnyObject 'がNSCopyingに準拠していない」というエラーが表示されます。

直接的な回答であろうと、Core Foundationタイプとの対話方法に関するガイダンスであろうと、どんな助けでもいただければ幸いです。

編集2

このソリューションは、Xcode 6 Beta 2以降は無効です。Beta1を使用している場合は、以下のコードが機能する可能性があります。

var secret: NSData = "Top Secret".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let query = NSDictionary(objects: [kSecClassGenericPassword, "MyService", "Some account", secret], forKeys: [kSecClass,kSecAttrService, kSecAttrAccount, kSecValueData])

OSStatus status = SecItemAdd(query as CFDictionaryRef, NULL)

キーチェーンアイテム属性キーをディクショナリキーとして使用するには、takeRetainedValueまたはtakeUnretainedValue(必要に応じて)を使用してそれらをアンラップする必要があります。次に、それらをNSCopyingにキャストできます。これは、ヘッダー内のCFTypeRefであり、すべてをコピーできるわけではないためです。

ただし、Xcode 6 Beta 2の時点では、これによりXcodeがクラッシュします。

13

Xcode 6.0.1では、これを行う必要があります!!

let kSecClassValue = NSString(format: kSecClass)
let kSecAttrAccountValue = NSString(format: kSecAttrAccount)
let kSecValueDataValue = NSString(format: kSecValueData)
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword)
let kSecAttrServiceValue = NSString(format: kSecAttrService)
let kSecMatchLimitValue = NSString(format: kSecMatchLimit)
let kSecReturnDataValue = NSString(format: kSecReturnData)
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne)
7
Beslan Tularov

リテラルをダウンキャストする必要があります。

let dict = ["hi": "Pasan"] as NSDictionary

現在、dictはNSDictionaryです。変更可能なものを作成するには、Objective-Cと非常によく似ています。

let mDict = dict.mutableCopy() as NSMutableDictionary
mDict["hola"] = "Ben"
13
Sam Soffes

おそらくそれ以来、状況は改善されています。 Xcode 7ベータ4では、結果AnyObject?を処理する場合を除いて、キャストは必要ないようです。具体的には、次のように機能します。

var query : [NSString : AnyObject] = [
    kSecClass : kSecClassGenericPassword,
    kSecAttrService : "MyAwesomeService",
    kSecReturnAttributes : true,   // return dictionary in result parameter
    kSecReturnData : true          // include the password value
]
var result : AnyObject?
let err = SecItemCopyMatching(query, &result)
if (err == errSecSuccess) {
    // on success cast the result to a dictionary and extract the
    // username and password from the dictionary.
    if let result = result as ? [NSString : AnyObject],
       let username = result[kSecAttrAccount] as? String,
       let passdata = result[kSecValueData] as? NSData,
       let password = NSString(data:passdata, encoding:NSUTF8StringEncoding) as? String {
        return (username, password)
    }
} else if (status == errSecItemNotFound) {
    return nil;
} else {
    // probably a program error,
    // print and lookup err code (e.g., -50 = bad parameter)
}

キーが欠落している場合に追加するには:

var query : [NSString : AnyObject] = [
    kSecClass : kSecClassGenericPassword,
    kSecAttrService : "MyAwesomeService",
    kSecAttrLabel : "MyAwesomeService Password",
    kSecAttrAccount : username,
    kSecValueData : password.dataUsingEncoding(NSUTF8StringEncoding)!
]
let result = SecItemAdd(query, nil)
// check that result is errSecSuccess, etc...

注意すべき点がいくつかあります。最初の問題は、str.dataUsingEncodingがオプションを返すことであった可能性があります。 '!'を追加するさらに良いことに、if letを使用してnilreturnを処理すると、コードが機能する可能性があります。エラーコードを印刷してドキュメントで調べると、問題を特定するのに大いに役立ちます(kSecClassの問題に気付くまで、データ型やキャストとは関係なく、エラー-50 =不正なパラメーターを取得していました!) 。

6
ɲeuroburɳ

これはうまく機能しているようでした、または少なくともコンパイラには子猫がいませんでした-それを超えてテストされていません

        var secret: NSData = "Top Secret".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
        var array1 = NSArray(objects:"\(kSecClassGenericPassword)", "MyService", "Some account", secret)
        var array2 = NSArray(objects:"\(kSecClass)","\(kSecAttrService)", "\(kSecAttrAccount)","\(kSecValueData)")
        let query = NSDictionary(objects: array1, forKeys: array2)
        println(query)
        let status  = SecItemAdd(query as CFDictionaryRef, nil)

ベータ2では正常に動作するようです

4
AppHandwerker

Swift

let kSecClassKey = String(kSecClass)
let kSecAttrAccountKey = String(kSecAttrAccount)
let kSecValueDataKey = String(kSecValueData)
let kSecClassGenericPasswordKey = String(kSecClassGenericPassword)
let kSecAttrServiceKey = String(kSecAttrService)
let kSecMatchLimitKey = String(kSecMatchLimit)
let kSecReturnDataKey = String(kSecReturnData)
let kSecMatchLimitOneKey = String(kSecMatchLimitOne)

辞書自体の中でそれを行うこともできます。

var query: [String: Any] = [
    String(kSecClass): kSecClassGenericPassword,
    String(kSecAttrService): "MyService",
    String(kSecAttrAccount): "Some account",
    String(kSecValueData): secret
]

ただし、これはコンパイラにとってよりコストがかかります。おそらく複数の場所でクエリを使用しているため、さらにコストがかかります。

1
Joe Susnick

これを機能させるには、代わりにキーチェーン定数の保持値にアクセスする必要があります。例えば:

let kSecClassValue = kSecClass.takeRetainedValue() as NSString
let kSecAttrAccountValue = kSecAttrAccount.takeRetainedValue() as NSString
let kSecValueDataValue = kSecValueData.takeRetainedValue() as NSString
let kSecClassGenericPasswordValue = kSecClassGenericPassword.takeRetainedValue() as NSString
let kSecAttrServiceValue = kSecAttrService.takeRetainedValue() as NSString
let kSecMatchLimitValue = kSecMatchLimit.takeRetainedValue() as NSString
let kSecReturnDataValue = kSecReturnData.takeRetainedValue() as NSString
let kSecMatchLimitOneValue = kSecMatchLimitOne.takeRetainedValue() as NSString

次に、次のようにMSMutableDictionaryの値を参照できます。

var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])

私はそれについてのブログ投稿を次の場所に書きました: http://rshelby.com/2014/08/using-Swift-to-save-and-query-ios-keychain-in-xcode-beta-4/

お役に立てれば!

rshelby

1
user3927134

カカオポッドSSKeychainを使用する方が便利

+ (NSArray *)allAccounts;
+ (NSArray *)accountsForService:(NSString *)serviceName;
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;
0
user4381618