Добавление элементов и запрос связки ключей iOS с помощью Swift
у меня возникли проблемы с преобразованием всех Objective C
примеры кода, доступные для добавления данных и запроса данных из iOS Keychain
на Свифт. Я пытаюсь сделать базовое хранилище строки (маркер доступа) и прочитать ее обратно. Я посмотрел на некоторые другие вопросы на переполнение стека, но я не могу заставить его работать. Я попытался собрать воедино решение из различных источников.
Edit 1: Я попытался с более базовой настройкой, потому что я думал о себе.defaultKeychainQuery, возможно, все испортил. Я обновил код ниже до последней версии.
Edit 2: он работает. Я не добавлял значение данных в запрос сохранения должным образом. Мне нужно было преобразовать строку в NSData. Я обновил код ниже до последней рабочей версии.
Edit 3: как Xerxes указывает ниже, этот код не работает с версиями Xcode выше бета-версии 1 из-за некоторые проблемы со словарями. Если вы знаете, как это исправить, пожалуйста, дайте мне знать.
Update: я превратил это в keychain библиотека написана в Swift называется слесарь.
сохранить
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, как только я его получил, или если у него есть какие-либо данные, приведенные выше.
5 ответов
чтобы заставить это работать, вам нужно будет получить сохраненные значения констант keychain и сохранить их сначала следующим образом:
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
Я написал демо-приложение и вспомогательные функции для этой простой задачи: написание/чтение текстовой строки для данного ключа в 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")
для пользователей Swift
однострочный код для добавления/извлечения / обновления полей в Keychain:
https://github.com/jrendel/SwiftKeychainWrapper
использование
добавить строковое значение в keychain:
let saveSuccessful: Bool = KeychainWrapper.setString("Some String", forKey: "myKey")
получить строковое значение из keychain:
let retrievedString: String? = KeychainWrapper.stringForKey("myKey")
удалить строковое значение из keychain:
let removeSuccessful: Bool = KeychainWrapper.removeObjectForKey("myKey")
моя интерпретация о том, как добавлять, получать, удалять пароли (для тех, кто ленив использовать библиотеки, представленные в этом потоке):
// 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/
Я думаю, что я разработал решение. Я отредактировал мой пост, чтобы включить код, который работает (по крайней мере для меня). Я также написал об этом здесь:использование связки ключей iOS с Swift (пример кода).
Update 11 Aug: я обновил код в блоге на основе комментариев rshelby. Взглянуть.
Update: я превратил это в keychain библиотека написана в Swift называется Слесарь!--7-->.
сохранить
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