import Foundation import Security enum KeychainStore { private static let service = "net.absdev.RsyncManager" static func string(for account: String) -> String { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: account, kSecReturnData as String: true, kSecMatchLimit as String: kSecMatchLimitOne ] var item: CFTypeRef? guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess, let data = item as? Data, let value = String(data: data, encoding: .utf8) else { return "" } return value } @discardableResult static func set(_ value: String, for account: String) -> OSStatus { let base: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: account ] if value.isEmpty { return delete(account) } let data = Data(value.utf8) let update: [String: Any] = [ kSecValueData as String: data, kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked ] let updateStatus = SecItemUpdate(base as CFDictionary, update as CFDictionary) if updateStatus != errSecItemNotFound { return updateStatus } var add = base add[kSecValueData as String] = data add[kSecAttrAccessible as String] = kSecAttrAccessibleWhenUnlocked return SecItemAdd(add as CFDictionary, nil) } @discardableResult static func delete(_ account: String) -> OSStatus { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: account ] let status = SecItemDelete(query as CFDictionary) return status == errSecItemNotFound ? errSecSuccess : status } }