import SwiftUI import BackgroundTasks extension Color { init(hex: String) { let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) var int: UInt64 = 0 Scanner(string: hex).scanHexInt64(&int) let a, r, g, b: UInt64 switch hex.count { case 3: (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) case 6: (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) case 8: (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) default: (a, r, g, b) = (255, 0, 0, 0) } self.init(.sRGB, red: Double(r)/255, green: Double(g)/255, blue: Double(b)/255, opacity: Double(a)/255) } } @main struct PulseApp: App { @StateObject private var authManager = AuthManager() init() { BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.daniil.pulsehealth.healthsync", using: nil) { task in Self.handleHealthSync(task: task as! BGProcessingTask) } } var body: some Scene { WindowGroup { Group { if authManager.isLoggedIn { MainTabView() } else { LoginView() } } .environmentObject(authManager) .onAppear { APIService.shared.authManager = authManager // Ensure health credentials are in Keychain if KeychainService.load(key: KeychainService.healthApiKeyKey) == nil { KeychainService.save(key: KeychainService.healthApiKeyKey, value: "health-cosmo-2026") HealthAPIService.configureCredentials(email: "daniilklimov25@gmail.com", password: "cosmo-health-2026") } authManager.healthApiKey = KeychainService.load(key: KeychainService.healthApiKeyKey) ?? "" Self.scheduleHealthSync() } } } static func scheduleHealthSync() { let request = BGProcessingTaskRequest(identifier: "com.daniil.pulsehealth.healthsync") request.earliestBeginDate = Date(timeIntervalSinceNow: 30 * 60) // 30 минут request.requiresNetworkConnectivity = true try? BGTaskScheduler.shared.submit(request) } static func handleHealthSync(task: BGProcessingTask) { // Запланировать следующий синк scheduleHealthSync() let syncTask = Task { let service = HealthKitService() let apiKey = KeychainService.load(key: KeychainService.healthApiKeyKey) ?? "" guard !apiKey.isEmpty else { return } try await service.syncToServer(apiKey: apiKey) } task.expirationHandler = { syncTask.cancel() } Task { do { try await syncTask.value task.setTaskCompleted(success: true) } catch { task.setTaskCompleted(success: false) } } } } class AuthManager: ObservableObject { @Published var isLoggedIn: Bool = false @Published var token: String = "" @Published var refreshToken: String = "" @Published var userName: String = "" @Published var userId: Int = 0 @Published var healthApiKey: String = "" init() { token = KeychainService.load(key: KeychainService.tokenKey) ?? "" refreshToken = KeychainService.load(key: KeychainService.refreshTokenKey) ?? "" healthApiKey = KeychainService.load(key: KeychainService.healthApiKeyKey) ?? "" userName = UserDefaults.standard.string(forKey: "userName") ?? "" userId = UserDefaults.standard.integer(forKey: "userId") isLoggedIn = !token.isEmpty } func login(token: String, refreshToken: String? = nil, user: UserInfo) { self.token = token self.refreshToken = refreshToken ?? "" self.userName = user.displayName self.userId = user.id KeychainService.save(key: KeychainService.tokenKey, value: token) if let rt = refreshToken { KeychainService.save(key: KeychainService.refreshTokenKey, value: rt) } UserDefaults.standard.set(user.displayName, forKey: "userName") UserDefaults.standard.set(user.id, forKey: "userId") isLoggedIn = true } func updateTokens(accessToken: String, refreshToken: String?) { self.token = accessToken KeychainService.save(key: KeychainService.tokenKey, value: accessToken) if let rt = refreshToken { self.refreshToken = rt KeychainService.save(key: KeychainService.refreshTokenKey, value: rt) } } func setHealthApiKey(_ key: String) { self.healthApiKey = key KeychainService.save(key: KeychainService.healthApiKeyKey, value: key) } func logout() { token = ""; refreshToken = ""; userName = ""; userId = 0 KeychainService.delete(key: KeychainService.tokenKey) KeychainService.delete(key: KeychainService.refreshTokenKey) UserDefaults.standard.removeObject(forKey: "userName") UserDefaults.standard.removeObject(forKey: "userId") isLoggedIn = false } }