Files
pulse-mobile/PulseHealth/App.swift
Daniil Klimov 0c21a14cb9 fix: remove Keychain accessGroup — fixes auth reset and health data
- Remove kSecAttrAccessGroup from KeychainService (requires entitlement
  that keeps getting stripped by Xcode)
- Basic Keychain works without accessGroup for the main app
- Fix health credentials migration check — use KeychainService.load directly
- Tokens now persist correctly between app launches

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:29:52 +03:00

136 lines
5.1 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}
}