Skip to content

Instantly share code, notes, and snippets.

@Coder-ACJHP
Created July 11, 2023 07:42
Show Gist options
  • Save Coder-ACJHP/e4d58ec5da565cf2ac84237673938dbe to your computer and use it in GitHub Desktop.
Save Coder-ACJHP/e4d58ec5da565cf2ac84237673938dbe to your computer and use it in GitHub Desktop.
Read, Update, Delete keys from keychain in swift
// Save into keychain
let nameData = Data(weparents_auth.utf8)
let passwordData = Data(weparents_PI.utf8)
KeychainHelper.shared.save(nameData, service: .userName, account: .account)
KeychainHelper.shared.save(passwordData, service: .password, account: .account)
// Read from keychain
if let nameData = KeychainHelper.shared.read(service: .userName, account: .account),
let passwordData = KeychainHelper.shared.read(service: .password, account: .account),
let nameString = String(data: nameData, encoding: .utf8),
let passwordString = String(data: passwordData, encoding: .utf8)
!nameString.isEmpty, !passwordString.isEmpty {
// Do what ever you want
}
enum Key: String {
case userName = "userName"
case password = "userPassword"
case account = "my.sweetApp.app.co"
}
final class KeychainHelper {
static let shared = KeychainHelper()
private init() {}
func save(_ data: Data, service: Key, account: Key) {
let query = [
kSecValueData: data,
kSecAttrService: service.rawValue,
kSecAttrAccount: account.rawValue,
kSecClass: kSecClassGenericPassword
] as CFDictionary
// Add data in query to keychain
let status = SecItemAdd(query, nil)
if status == errSecDuplicateItem {
// Item already exist, thus update it.
let query = [
kSecAttrService: service.rawValue,
kSecAttrAccount: account.rawValue,
kSecClass: kSecClassGenericPassword,
] as CFDictionary
let attributesToUpdate = [kSecValueData: data] as CFDictionary
// Update existing item
SecItemUpdate(query, attributesToUpdate)
}
}
func read(service: Key, account: Key) -> Data? {
let query = [
kSecAttrService: service.rawValue,
kSecAttrAccount: account.rawValue,
kSecClass: kSecClassGenericPassword,
kSecReturnData: true
] as CFDictionary
var result: AnyObject?
SecItemCopyMatching(query, &result)
return (result as? Data)
}
func delete(service: Key, account: Key) {
let query = [
kSecAttrService: service.rawValue,
kSecAttrAccount: account.rawValue,
kSecClass: kSecClassGenericPassword,
] as CFDictionary
// Delete item from keychain
SecItemDelete(query)
}
}
extension KeychainHelper {
func save<T>(_ item: T, service: Key, account: Key) where T : Codable {
do {
// Encode as JSON data and save in keychain
let data = try JSONEncoder().encode(item)
save(data, service: service, account: account)
} catch {
assertionFailure("Fail to encode item for keychain: \(error)")
}
}
func read<T>(service: Key, account: Key, type: T.Type) -> T? where T : Codable {
// Read item data from keychain
guard let data = read(service: service, account: account) else {
return nil
}
// Decode JSON data to object
do {
let item = try JSONDecoder().decode(type, from: data)
return item
} catch {
assertionFailure("Fail to decode item for keychain: \(error)")
return nil
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment