我无法转换所有可用于添加数据并将数据从Objective C
查询到 Swift 的iOS Keychain
代码示例。我试图对字符串(访问令牌)进行基本存储并将其读回。我已经看过Stack Overflow上的其他一些问题,但我无法让它发挥作用。我试图将各种来源的解决方案拼凑起来。
编辑1:我尝试了一个更基本的设置,因为我认为我的self.defaultKeychainQuery可能搞砸了。我已将以下代码更新为最新版本。
编辑2:让它正常工作。我没有正确地将数据值添加到保存查询中。我需要将字符串转换为NSData。我已将下面的代码更新为最新的工作版本。
编辑3:正如Xerxes指出的那样,由于字典存在一些问题,此代码不适用于高于Beta 1的Xcode版本。如果你知道修复此问题,请告诉我。
保存
class func save(service: NSString, data: NSString) {
var dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
// Instantiate a new default keychain query
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, userAccount, dataFromString], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecValueData])
// Delete any existing items
SecItemDelete(keychainQuery as CFDictionaryRef)
// Add the new keychain item
var status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil)
// Check that it worked ok
println("Saving status code is: \(status)")
}
加载
class func load(service: NSString) -> AnyObject? {
// Instantiate a new default keychain query
// Tell the query to return a result
// Limit our results to one item
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, userAccount, kCFBooleanTrue, kSecMatchLimitOne], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecReturnData, kSecMatchLimit])
// I'm not too sure what's happening here...
var dataTypeRef :Unmanaged<AnyObject>?
// Search for the keychain items
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
println("Loading status code is: \(status)")
// I'm not too sure what's happening here...
let opaque = dataTypeRef?.toOpaque()
if let op = opaque? {
let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
println("Retrieved the following data from the keychain: \(retrievedData)")
var str = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
println("The decoded string is \(str)")
} else {
println("Nothing was retrieved from the keychain.")
}
return nil
}
用法(视图控制器)
KeychainService.saveToken("sometoken")
KeychainService.loadToken()
使用这些便利方法
class func saveToken(token: NSString) {
self.save("service", data: token)
}
class func loadToken() {
var token = self.load("service")
if let t = token {
println("The token is: \(t)")
}
}
这导致控制台中的输出:
Saving status code is: 0
Loading status code is: 0
Retrieved the following data from the keychain: <736f6d65 746f6b65 6e>
The decoded string is sometoken
非常感谢你的帮助。我不太确定在获得dataTypeRef之后该如何处理,或者如果上面的代码有任何数据。
答案 0 :(得分:7)
为了使其工作,你需要检索钥匙串常量的保留值,然后先存储,如下所示:
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
然后您可以像这样引用NSMutableDictionary中的值:
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 :(得分:5)
我为这个简单的任务编写了一个演示应用程序和辅助函数:在Keychain中写入/读取给定键的文本字符串。
https://github.com/marketplacer/keychain-swift
let keychain = KeychainSwift()
keychain.set("hello world", forKey: "my key")
keychain.get("my key")
keychain.delete("my key")
答案 2 :(得分:3)
对于Swift用户
在钥匙串中添加/检索/更新字段的单行代码:
https://github.com/jrendel/SwiftKeychainWrapper
<强>用法强>
将字符串值添加到钥匙串:
final AutoCompleteTextView textView = (AutoCompleteTextView) v.findViewById(R.id.listview4);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_spinner_dropdown_item, somearray);
textView.setTextColor(Color.BLACK);
textView.setThreshold(1);
textView.setAdapter(adapter);
从钥匙串中检索字符串值:
let saveSuccessful: Bool = KeychainWrapper.setString("Some String", forKey: "myKey")
从钥匙串中删除字符串值:
let retrievedString: String? = KeychainWrapper.stringForKey("myKey")
答案 3 :(得分:3)
我对如何添加,获取,删除密码的解释(对于那些懒惰使用此线程中提供的库的人):
// Saving password associated with the login and service
let userAccount = "user's login"
let service = "service name"
let passwordData: NSData = self.textfield_password.text!.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
let keychainQuery: [NSString: NSObject] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: userAccount,
kSecAttrService: service,
kSecValueData: passwordData]
SecItemDelete(keychainQuery as CFDictionaryRef) //Deletes the item just in case it already exists
let keychain_save_status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil)
print("Keychain saving code is: \(keychain_save_status)")
...
// Getting the password associated with the login and service
let userAccount = "user's login"
let service = "service name"
let keychainQuery: [NSString: NSObject] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: userAccount,
kSecReturnData: kCFBooleanTrue,
kSecMatchLimit: kSecMatchLimitOne]
var rawResult: AnyObject?
let keychain_get_status: OSStatus = SecItemCopyMatching(keychainQuery, &rawResult)
print("Keychain getting code is: \(keychain_get_status)")
if (keychain_get_status == errSecSuccess) {
let retrievedData = rawResult as? NSData
let pass = NSString(data: retrievedData!, encoding: NSUTF8StringEncoding)
print("Username: \(userAccount), password: \(pass!)")
// Do your work with the retrieved password here
} else {
print("No login data found in Keychain.")
...
//Deleting user's credentials from Keychain. Password is optional for the query when you delete, in most cases you won't know it after all.
let userAccount = "user's login"
let service = "service name"
let keychainQuery: [NSString: NSObject] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: userAccount,
kSecAttrService: service]
let keychain_delete_status: OSStatus = SecItemDelete(keychainQuery as CFDictionaryRef)
print("Keychain deleting code is: \(keychain_delete_status)")
结果代码和其他有用信息可在官方文档中找到:https://developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/
答案 4 :(得分:1)
我想我已经找到了解决方案。我已经编辑了上面的帖子,包括有效的代码(至少对我而言)。我也在这里写了博客:using the iOS Keychain with Swift (example code)。
8月11日更新:我根据rshelby的评论更新了博客文章中的代码。看一看。
保存强>
class func save(service: NSString, data: NSString) {
var dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
// Instantiate a new default keychain query
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, userAccount, dataFromString], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecValueData])
// Delete any existing items
SecItemDelete(keychainQuery as CFDictionaryRef)
// Add the new keychain item
var status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil)
// Check that it worked ok
println("Saving status code is: \(status)")
}
<强>加载强>
class func load(service: NSString) -> AnyObject? {
// Instantiate a new default keychain query
// Tell the query to return a result
// Limit our results to one item
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, userAccount, kCFBooleanTrue, kSecMatchLimitOne], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecReturnData, kSecMatchLimit])
// I'm not too sure what's happening here...
var dataTypeRef :Unmanaged<AnyObject>?
// Search for the keychain items
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
println("Loading status code is: \(status)")
// I'm not too sure what's happening here...
let opaque = dataTypeRef?.toOpaque()
if let op = opaque? {
let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
println("Retrieved the following data from the keychain: \(retrievedData)")
var str = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
println("The decoded string is \(str)")
} else {
println("Nothing was retrieved from the keychain.")
}
return nil
}
用法(视图控制器)
KeychainService.saveToken("sometoken")
KeychainService.loadToken()
使用这些便利方法
class func saveToken(token: NSString) {
self.save("service", data: token)
}
class func loadToken() {
var token = self.load("service")
if let t = token {
println("The token is: \(t)")
}
}
这导致控制台中的输出:
Saving status code is: 0
Loading status code is: 0
Retrieved the following data from the keychain: <736f6d65 746f6b65 6e>
The decoded string is sometoken