5bc81d5b3b
iOS SwiftUI app with Supabase auth/realtime, Node.js backend, Docker/Supabase self-hosted infrastructure, and APNs scheduler. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
44 lines
1.3 KiB
Swift
44 lines
1.3 KiB
Swift
import Foundation
|
|
import Security
|
|
|
|
final class KeychainService {
|
|
static let shared = KeychainService()
|
|
private let service = "app.nightly"
|
|
private let account = "authToken"
|
|
|
|
func saveToken(_ token: String) {
|
|
let data = Data(token.utf8)
|
|
let query: [CFString: Any] = [
|
|
kSecClass: kSecClassGenericPassword,
|
|
kSecAttrService: service,
|
|
kSecAttrAccount: account,
|
|
kSecValueData: data
|
|
]
|
|
SecItemDelete(query as CFDictionary)
|
|
SecItemAdd(query as CFDictionary, nil)
|
|
}
|
|
|
|
func getToken() -> String? {
|
|
let query: [CFString: Any] = [
|
|
kSecClass: kSecClassGenericPassword,
|
|
kSecAttrService: service,
|
|
kSecAttrAccount: account,
|
|
kSecReturnData: true,
|
|
kSecMatchLimit: kSecMatchLimitOne
|
|
]
|
|
var item: CFTypeRef?
|
|
guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess,
|
|
let data = item as? Data else { return nil }
|
|
return String(data: data, encoding: .utf8)
|
|
}
|
|
|
|
func deleteToken() {
|
|
let query: [CFString: Any] = [
|
|
kSecClass: kSecClassGenericPassword,
|
|
kSecAttrService: service,
|
|
kSecAttrAccount: account
|
|
]
|
|
SecItemDelete(query as CFDictionary)
|
|
}
|
|
}
|