- Auth: переключено на Pulse API (api.digital-home.site) вместо health - TabBar: Главная, Задачи, Привычки, Здоровье, Финансы - Models: TaskModels, HabitModels, FinanceModels, обновлённые AuthModels - Services: APIService (Pulse API), HealthAPIService (health отдельно) - Dashboard: обзор дня с задачами, привычками, readiness, балансом - Tasks: список, фильтр, создание, выполнение, удаление - Habits: список с прогресс-баром, отметка выполнения, стрики - Health: бывший DashboardView, HealthKit sync через health API key - Finance: баланс, список транзакций, добавление расхода/дохода - Health данные через x-api-key вместо JWT токена health сервиса
61 lines
2.9 KiB
Swift
61 lines
2.9 KiB
Swift
import Foundation
|
|
|
|
class HealthAPIService {
|
|
static let shared = HealthAPIService()
|
|
let baseURL = "https://health.digital-home.site"
|
|
|
|
private func makeRequest(_ path: String, token: String? = nil, apiKey: String? = nil) -> URLRequest {
|
|
var req = URLRequest(url: URL(string: "\(baseURL)\(path)")!)
|
|
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
req.timeoutInterval = 15
|
|
if let t = token { req.setValue("Bearer \(t)", forHTTPHeaderField: "Authorization") }
|
|
if let k = apiKey { req.setValue(k, forHTTPHeaderField: "x-api-key") }
|
|
return req
|
|
}
|
|
|
|
func getLatest(apiKey: String) async throws -> LatestHealthResponse {
|
|
let req = makeRequest("/api/health/latest", apiKey: apiKey)
|
|
let (data, response) = try await URLSession.shared.data(for: req)
|
|
guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {
|
|
throw APIError.networkError("Latest недоступен")
|
|
}
|
|
return try JSONDecoder().decode(LatestHealthResponse.self, from: data)
|
|
}
|
|
|
|
func getReadiness(apiKey: String) async throws -> ReadinessResponse {
|
|
let req = makeRequest("/api/health/readiness", apiKey: apiKey)
|
|
let (data, response) = try await URLSession.shared.data(for: req)
|
|
guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {
|
|
throw APIError.networkError("Readiness недоступен")
|
|
}
|
|
return try JSONDecoder().decode(ReadinessResponse.self, from: data)
|
|
}
|
|
|
|
func getHeatmap(apiKey: String, days: Int = 7) async throws -> [HeatmapEntry] {
|
|
let req = makeRequest("/api/health/heatmap?days=\(days)", apiKey: apiKey)
|
|
let (data, response) = try await URLSession.shared.data(for: req)
|
|
guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {
|
|
throw APIError.networkError("Heatmap недоступен")
|
|
}
|
|
if let entries = try? JSONDecoder().decode([HeatmapEntry].self, from: data) {
|
|
return entries
|
|
}
|
|
let wrapped = try JSONDecoder().decode(HeatmapResponse.self, from: data)
|
|
return wrapped.data
|
|
}
|
|
|
|
func sendHealthData(apiKey: String, payload: Data) async throws {
|
|
let url = URL(string: "\(baseURL)/api/health?key=\(apiKey)")!
|
|
var req = URLRequest(url: url)
|
|
req.httpMethod = "POST"
|
|
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
req.httpBody = payload
|
|
req.timeoutInterval = 30
|
|
let (_, response) = try await URLSession.shared.data(for: req)
|
|
guard let http = response as? HTTPURLResponse, (200...299).contains(http.statusCode) else {
|
|
let code = (response as? HTTPURLResponse)?.statusCode ?? 0
|
|
throw APIError.serverError(code, "Ошибка отправки health данных")
|
|
}
|
|
}
|
|
}
|