Created
May 24, 2017 15:27
-
-
Save lucianomarisi/d8b2ec5d26abde211fa7164c6adc4b75 to your computer and use it in GitHub Desktop.
Simple class for storing Strings in the Keychain
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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