payfrit-food-ios/PayfritFood/ViewModels/AppState.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

119 lines
3.2 KiB
Swift

import SwiftUI
@MainActor
class AppState: ObservableObject {
// MARK: - Authentication State
@Published var isAuthenticated = false
@Published var userId: Int?
@Published var userToken: String?
@Published var userProfile: UserProfile?
@Published var isPremium = false
// MARK: - Current Product State
@Published var currentProduct: Product?
@Published var currentAlternatives: [Alternative] = []
// MARK: - Navigation State
@Published var selectedTab: Tab = .scan
@Published var showLoginSheet = false
@Published var showRegisterSheet = false
// MARK: - Loading State
@Published var isLoading = false
@Published var errorMessage: String?
enum Tab: Int, CaseIterable {
case scan = 0
case favorites = 1
case history = 2
case account = 3
var title: String {
switch self {
case .scan: return "Scan"
case .favorites: return "Favorites"
case .history: return "History"
case .account: return "Account"
}
}
var icon: String {
switch self {
case .scan: return "barcode.viewfinder"
case .favorites: return "heart.fill"
case .history: return "clock.fill"
case .account: return "person.fill"
}
}
}
// MARK: - Initialization
init() {
Task {
await loadSavedAuth()
}
}
// MARK: - Authentication
func loadSavedAuth() async {
guard let credentials = await AuthStorage.shared.loadCredentials() else { return }
self.userId = credentials.userId
self.userToken = credentials.token
await APIService.shared.setToken(credentials.token)
// Try to fetch profile
do {
let profile = try await APIService.shared.getProfile()
self.userProfile = profile
self.isPremium = profile.isPremium
self.isAuthenticated = true
} catch {
// Token may be expired
await clearAuth()
}
}
func setAuth(userId: Int, token: String, profile: UserProfile) async {
self.userId = userId
self.userToken = token
self.userProfile = profile
self.isPremium = profile.isPremium
self.isAuthenticated = true
await APIService.shared.setToken(token)
await AuthStorage.shared.saveCredentials(AuthCredentials(userId: userId, token: token))
}
func clearAuth() async {
self.userId = nil
self.userToken = nil
self.userProfile = nil
self.isPremium = false
self.isAuthenticated = false
await APIService.shared.clearToken()
await AuthStorage.shared.clearAll()
}
// MARK: - Product Actions
func setCurrentProduct(_ product: Product) {
self.currentProduct = product
self.currentAlternatives = []
}
func clearCurrentProduct() {
self.currentProduct = nil
self.currentAlternatives = []
}
// MARK: - Error Handling
func showError(_ message: String) {
self.errorMessage = message
Task {
try? await Task.sleep(nanoseconds: 3_000_000_000)
self.errorMessage = nil
}
}
}