SHA256
Introduce real SwiftUI auth/dashboard/task/note/shopping screens, a bounded origin-bound URLSession transport and Keychain-backed native sessions. Archive the rejected WebKit renderer rather than embedding it for unfinished modules. Sixteen portable URL/contract/transport tests and Swift format/parsing pass on Linux; Apple SDK application and UI validation remains mandatory. This is an incomplete native slice, not full PWA parity or a release.
41 lines
1.6 KiB
Swift
41 lines
1.6 KiB
Swift
import Foundation
|
|
import Security
|
|
|
|
/// Native cookies, separate from WebKit and scoped to one configured origin.
|
|
struct NativeSessionVault: Sendable {
|
|
let origin: String
|
|
private var query: [String: Any] {
|
|
[
|
|
kSecClass as String: kSecClassGenericPassword,
|
|
kSecAttrService as String: "community.yuvomi.mobile.native-session",
|
|
kSecAttrAccount as String: origin,
|
|
]
|
|
}
|
|
func load() -> Data? {
|
|
var request = query
|
|
request[kSecReturnData as String] = true
|
|
request[kSecMatchLimit as String] = kSecMatchLimitOne
|
|
var result: CFTypeRef?
|
|
guard SecItemCopyMatching(request as CFDictionary, &result) == errSecSuccess else { return nil }
|
|
return result as? Data
|
|
}
|
|
func save(_ data: Data) throws {
|
|
let attributes: [String: Any] = [
|
|
kSecValueData as String: data,
|
|
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
|
]
|
|
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
|
|
if status == errSecItemNotFound {
|
|
var request = query
|
|
for (key, value) in attributes { request[key] = value }
|
|
guard SecItemAdd(request as CFDictionary, nil) == errSecSuccess else { throw NativeAPIError.unavailable }
|
|
} else if status != errSecSuccess {
|
|
throw NativeAPIError.unavailable
|
|
}
|
|
}
|
|
func clear() throws {
|
|
let status = SecItemDelete(query as CFDictionary)
|
|
guard status == errSecSuccess || status == errSecItemNotFound else { throw NativeAPIError.unavailable }
|
|
}
|
|
}
|