使用Swift将项添加到钥匙串

我正在尝试使用Swift将项添加到iOS钥匙串,但无法弄清楚如何正确输入。 从WWDC 2013会议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’。

我采用的另一种方法是在Dictionary上使用Swift和- setObject:forKey:方法,使用密钥kSecClass添加kSecClassGenericPassword。

在Objective-C中:

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

在Objective-C代码中,使用id桥接各种keychain项类键的CFTypeRef。 在Swift 文档中 ,提到Swift将id作为AnyObject导入。 但是,当我尝试将kSecClass转发为方法的AnyObject时,我得到的错误是“Type’AnyObject’不符合NSCopying。

任何帮助,无论是直接答案还是有关如何与Core Foundation类型交互的指导,都将不胜感激。

编辑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 Attribute键作为字典键,您必须使用takeRetainedValue或takeUnretainedValue(根据需要)将它们打开。 然后你可以将它们转换为NSCopying。 这是因为它们在标头中是CFTypeRefs,它们都不是可复制的。

但是,从Xcode 6 Beta 2开始,这会导致Xcode崩溃。

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

你只需要向下翻译文字:

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

现在dict是一个NSDictionary。 要制作一个可变的,它与Objective-C非常相似:

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

也许事情有所改善。 在Xcode 7 beta 4上,除了处理结果AnyObject?之外,似乎没有必要进行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 (eg, -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返回Optional。 添加’!’ 或者更好的是,使用if let来处理nil返回,可能会使你的代码工作。 打印出错误代码并在文档中查找它将有助于隔离问题(我得到的错误-50 =错误的参数,直到我注意到我的kSecClass有问题,与数据类型或强制转换无关!) 。

这似乎工作得很好,或者至少编译器没有小猫 – 除此之外没有尝试过

  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中工作正常

为了使其工作,您需要访问钥匙串常量的保留值。 例如:

 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

斯威夫特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) 

你也可以在字典里面这样做:

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

但是,这对于编译器来说更贵,因为您可能在多个地方使用查询。

更方便使用cocoa豆荚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;