a31bc807cb
SwiftUI app for the MeshMonitor REST API: setup screen with token storage in Keychain, welcome dashboard mirroring the Emacs welcome buffer, and channel/node/DM/unread lists with a shared chat view supporting both channel and direct messages.
37 lines
1.1 KiB
Swift
37 lines
1.1 KiB
Swift
import Foundation
|
|
import Security
|
|
|
|
enum KeychainStore {
|
|
private static let service = "dev.andros.meshmonitorchat"
|
|
|
|
static func set(_ value: String?, for key: String) {
|
|
let q: [String: Any] = [
|
|
kSecClass as String: kSecClassGenericPassword,
|
|
kSecAttrService as String: service,
|
|
kSecAttrAccount as String: key
|
|
]
|
|
SecItemDelete(q as CFDictionary)
|
|
guard let value, let data = value.data(using: .utf8) else { return }
|
|
var attrs = q
|
|
attrs[kSecValueData as String] = data
|
|
attrs[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
|
|
SecItemAdd(attrs as CFDictionary, nil)
|
|
}
|
|
|
|
static func get(_ key: String) -> String? {
|
|
let q: [String: Any] = [
|
|
kSecClass as String: kSecClassGenericPassword,
|
|
kSecAttrService as String: service,
|
|
kSecAttrAccount as String: key,
|
|
kSecReturnData as String: true,
|
|
kSecMatchLimit as String: kSecMatchLimitOne
|
|
]
|
|
var result: AnyObject?
|
|
let status = SecItemCopyMatching(q as CFDictionary, &result)
|
|
guard status == errSecSuccess,
|
|
let data = result as? Data,
|
|
let s = String(data: data, encoding: .utf8) else { return nil }
|
|
return s
|
|
}
|
|
}
|