rastrillo / native Public

Clone
git clone https://amadan.net/rastrillo/native

Plain git — no account needed to clone.

Download

Download this file

1import Foundation
2import Security
3
4enum NativeCredentials {
5 private static var service: String { (Bundle.main.bundleIdentifier ?? "oficina.native") + ".session" }
6 private static func query(_ origin: String) -> [String: Any] {
7 [kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service,
8 kSecAttrAccount as String: origin]
9 }
10 static func read(_ origin: String) -> NativeSession? {
11 var q = query(origin)
12 q[kSecReturnData as String] = true
13 q[kSecMatchLimit as String] = kSecMatchLimitOne
14 var result: CFTypeRef?
15 guard SecItemCopyMatching(q as CFDictionary, &result) == errSecSuccess,
16 let data = result as? Data else { return nil }
17 return try? JSONDecoder().decode(NativeSession.self, from: data)
18 }
19 static func save(_ session: NativeSession, origin: String) throws {
20 let data = try JSONEncoder().encode(session)
21 let updated = SecItemUpdate(query(origin) as CFDictionary, [kSecValueData as String: data] as CFDictionary)
22 if updated == errSecSuccess { return }
23 guard updated == errSecItemNotFound else { throw NativeClientError.message("Could not update this session in Keychain.") }
24 var q = query(origin)
25 q[kSecValueData as String] = data
26 q[kSecAttrAccessible as String] = kSecAttrAccessibleWhenUnlockedThisDeviceOnly
27 guard SecItemAdd(q as CFDictionary, nil) == errSecSuccess else {
28 throw NativeClientError.message("Could not save this session in Keychain.")
29 }
30 }
31 static func remove(_ origin: String) { SecItemDelete(query(origin) as CFDictionary) }
32}
33