| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071 |
- import Foundation
- import Security
- /// Minimal Keychain wrapper for storing the Chad Music API key.
- enum KeychainService {
- private static let service = "com.mixboard.chad-music"
- private static let apiKeyAccount = "api-key"
- // MARK: - API Key
- static func saveAPIKey(_ key: String) throws {
- guard let data = key.data(using: .utf8) else { return }
- // Delete existing item first
- let deleteQuery: [String: Any] = [
- kSecClass as String: kSecClassGenericPassword,
- kSecAttrService as String: service,
- kSecAttrAccount as String: apiKeyAccount,
- ]
- SecItemDelete(deleteQuery as CFDictionary)
- // Add new item
- let addQuery: [String: Any] = [
- kSecClass as String: kSecClassGenericPassword,
- kSecAttrService as String: service,
- kSecAttrAccount as String: apiKeyAccount,
- kSecValueData as String: data,
- kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked,
- ]
- let status = SecItemAdd(addQuery as CFDictionary, nil)
- guard status == errSecSuccess else {
- throw KeychainError.saveFailed(status)
- }
- }
- static func loadAPIKey() -> String? {
- let query: [String: Any] = [
- kSecClass as String: kSecClassGenericPassword,
- kSecAttrService as String: service,
- kSecAttrAccount as String: apiKeyAccount,
- kSecReturnData as String: true,
- kSecMatchLimit as String: kSecMatchLimitOne,
- ]
- var result: AnyObject?
- let status = SecItemCopyMatching(query as CFDictionary, &result)
- guard status == errSecSuccess, let data = result as? Data else { return nil }
- return String(data: data, encoding: .utf8)
- }
- static func deleteAPIKey() {
- let query: [String: Any] = [
- kSecClass as String: kSecClassGenericPassword,
- kSecAttrService as String: service,
- kSecAttrAccount as String: apiKeyAccount,
- ]
- SecItemDelete(query as CFDictionary)
- }
- // MARK: - Errors
- enum KeychainError: LocalizedError {
- case saveFailed(OSStatus)
- var errorDescription: String? {
- switch self {
- case .saveFailed(let status):
- "Keychain save failed with status \(status)"
- }
- }
- }
- }
|