payfrit-food-ios/PayfritFood/Services/AuthStorage.swift
John Pinkyfloyd 71e7ec34f6 Initial commit: PayfritFood iOS app
- SwiftUI + async/await architecture
- Barcode scanning with AVFoundation
- Product display with score ring, NOVA badge, nutrition
- Alternatives with sort/filter
- Auth (login/register)
- Favorites & history
- Account management
- Dark theme
- Connected to food.payfrit.com API (Open Food Facts proxy)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-16 16:58:21 -07:00

86 lines
2.6 KiB
Swift

import Foundation
import Security
struct AuthCredentials {
let userId: Int
let token: String
}
actor AuthStorage {
static let shared = AuthStorage()
private let serviceName = "com.payfrit.food"
private let tokenKey = "userToken"
private let userIdKey = "userId"
// MARK: - Public Interface
func saveCredentials(_ credentials: AuthCredentials) {
saveToKeychain(key: tokenKey, value: credentials.token)
saveToKeychain(key: userIdKey, value: String(credentials.userId))
UserDefaults.standard.set(credentials.userId, forKey: userIdKey)
}
func loadCredentials() -> AuthCredentials? {
guard let token = loadFromKeychain(key: tokenKey),
let userIdString = loadFromKeychain(key: userIdKey),
let userId = Int(userIdString) else {
return nil
}
return AuthCredentials(userId: userId, token: token)
}
func clearAll() {
deleteFromKeychain(key: tokenKey)
deleteFromKeychain(key: userIdKey)
UserDefaults.standard.removeObject(forKey: userIdKey)
}
// MARK: - Keychain Operations
private func saveToKeychain(key: String, value: String) {
let data = value.data(using: .utf8)!
// Delete existing item first
deleteFromKeychain(key: key)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: key,
kSecValueData as String: data,
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock
]
SecItemAdd(query as CFDictionary, nil)
}
private func loadFromKeychain(key: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: key,
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,
let value = String(data: data, encoding: .utf8) else {
return nil
}
return value
}
private func deleteFromKeychain(key: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: key
]
SecItemDelete(query as CFDictionary)
}
}