Skip to content

Instantly share code, notes, and snippets.

@lucianomarisi
Created May 24, 2017 15:27
Show Gist options
  • Save lucianomarisi/d8b2ec5d26abde211fa7164c6adc4b75 to your computer and use it in GitHub Desktop.
Save lucianomarisi/d8b2ec5d26abde211fa7164c6adc4b75 to your computer and use it in GitHub Desktop.
Simple class for storing Strings in the Keychain
import Foundation
final class KeychainStore {
func string(for key: String) -> String? {
return self.readItem(forKey: key)
}
func setString(_ string: String?, for key: String) {
guard let string = string else {
deleteItem(key: key)
return
}
if self.readItem(forKey: key) == nil {
createItem(string, forKey: key)
} else {
updateItem(string, forKey: key)
}
}
// MARK: Keychain CRUD
private func createItem(_ item: String, forKey key: String) {
var newItem = self.query(key: key)
newItem[kSecValueData as String] = item.data(using: .utf8)
_ = SecItemAdd(newItem as CFDictionary, nil)
}
private func readItem(forKey key: String) -> String? {
var query = self.query(key: key)
query[kSecMatchLimit as String] = kSecMatchLimitOne
query[kSecReturnAttributes as String] = kCFBooleanTrue
query[kSecReturnData as String] = kCFBooleanTrue
var queryResult: AnyObject?
let status = withUnsafeMutablePointer(to: &queryResult) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
guard
status == noErr,
let existingItem = queryResult as? [String: Any],
let stringData = existingItem[kSecValueData as String] as? Data,
let string = String(data: stringData, encoding: .utf8)
else {
return nil
}
return string
}
private func updateItem(_ item: String, forKey key: String) {
var attributesToUpdate = [String: Any]()
attributesToUpdate[kSecValueData as String] = item.data(using: .utf8)
let query = self.query(key: key)
_ = SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary)
}
private func deleteItem(key: String) {
let query = self.query(key: key)
_ = SecItemDelete(query as CFDictionary)
}
private func query(key: String) -> [String: Any] {
var query = [String: Any]()
query[kSecClass as String] = kSecClassGenericPassword
let serviceName = Bundle.main.bundleIdentifier ?? "DefaultServiceName"
query[kSecAttrService as String] = serviceName
let encodedKey: Data? = key.data(using: .utf8)
query[kSecAttrGeneric as String] = encodedKey
query[kSecAttrAccount as String] = encodedKey
return query
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment