2014-06-09 42 views
11

我想使用Swift將項目添加到iOS鑰匙串,但無法弄清楚如何正確鍵入轉換。如下使用Swift將項目添加到鑰匙串

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); 

試圖做到這一點在斯威夫特:

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

產生了錯誤「無法表達的類型‘詞典’轉換爲從WWDC 2013屆709,給出下面的Objective-C代碼「DictionaryLiteralConvertible」。

我把另一種方法是用斯威夫特和字典的- 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的方法downcast時,我得到的錯誤是「Type'AnyObject'不符合NSCopying。

任何幫助,無論是直接的答案還是關於如何與Core進行交互的指導基金類型,將不勝感激。

EDIT 2

這個解決方案是不作爲的Xcode 6 Beta 2版的如果您正在使用Beta 1的下面的代碼可以工作更長時間有效。

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) 

要使用Keychain Item屬性鍵作爲字典鍵,必須使用takeRetainedValue或takeUnretainedValue(根據需要)解開它們。然後你可以將它們投射到NSCopying。這是因爲它們是頭中的CFTypeRefs,它們不是全部可複製的。

但是從Xcode 6 Beta 2開始,這會導致Xcode崩潰。

+0

嗨。你能否讓我知道下面是如何被接受的答案?我有與上面顯示的相同的代碼,但無論我做什麼,我都無法讀取或向KeyChain添加項目。您是否可以使用解決方案更新代碼,以確定您是如何專門將項目保存在鑰匙串中的?提前致謝。 – Darren

+1

@達倫。我編輯了我的問題以包含答案。在第二次編輯之前,我接受了下面的答案,所以我沒有試圖通過向下轉換文字來實現它。 –

+0

@PasanPremaratne嗨,我正在嘗試做同樣的事情,但它在Beta 2中不起作用,您是否找到了解決方案?謝謝 – Xerxes

回答

7

在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) 
+2

你能解釋一下你的答案嗎? – Zulu

+1

調用讓kSecClassValue = kSecClass.takeRetainedValue()作爲NSString將生成一個錯誤,因爲類kSecClass沒有方法takeRetainedValue() ,因此常量聲明kSekslassValue應該看起來像這樣,如上所述 –

+0

在答案會更好 – Zulu

13

您只需向下轉型文字:

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

現在dict是一個NSDictionary。爲了使一個可變的一個,這是非常相似的Objective-C:

let mDict = dict.mutableCopy() as NSMutableDictionary 
mDict["hola"] = "Ben" 
+0

爲什麼我不得不沮喪文字,你可以解釋一下。因爲我認爲當我們在同一個地方聲明和分配一個變量時,我們不需要施加任何變量。 –

+0

這仍然不適合我。在問題示例中使用swift代碼的形式時,我會得到相同的(或錯誤消息的變體)。 – Darren

+0

嗨,大家好,我正在嘗試做同樣的事情,但它在Beta 2中不起作用,您是否找到了解決方案?謝謝 – Xerxes

4

這似乎很好地工作,或者至少編譯器沒有小貓 - UNTESTED超出

 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) 

看起來工作得很好在Beta 2中

1

爲了得到這個工作,您需要訪問鑰匙串常量的保留值。例如:

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像這樣: http://rshelby.com/2014/08/using-swift-to-save-and-query-ios-keychain-in-xcode-beta-4/

希望這有助於:

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

我在寫一個關於它的博客帖子!

rshelby

0

使用更方便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

我知道SSKeychain(實際上這裏提供的答案之一是由作者自己提供的),但我正在尋找一個純Swift實現,而不是使用橋接標頭 –

+0

@pasan正在尋求純Swift實現。它也很明顯,他想學習如何自己做。 – Chris

5

也許事情已經因爲改善。在Xcode 7 beta 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來處理零回報,可能會使你的代碼工作。打印錯誤代碼並在文檔中查找將有助於隔離問題(我得到了err -50 = bad參數,直到我發現我的kSecClass有問題,與數據類型或演員無關!) 。

+0

這幫了我,我一直在尋找。不是不必要地使用依賴關係的粉絲。謝謝! – Chris

1

斯威夫特3

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) 

你也可以做到這一點的字典本身ALA內:

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

然而,這是編譯器更昂貴,更是這樣,因爲你可能在多個地方使用查詢。

相關問題