// // WorkoutSettings.swift // Keging // // 세트 구성(수축·이완·횟수)과 진동 패턴 — 폰에서 설정, 워치로 동기화 // import Foundation import Combine /// 진동 패턴 — 수축/이완 각각 선택. 진동 횟수·리듬으로 구분한다 nonisolated enum HapticPattern: String, CaseIterable, Identifiable, Codable { case once, twice, thrice, heartbeat var id: String { rawValue } var label: String { switch self { case .once: "한 번" case .twice: "두 번" case .thrice: "세 번" case .heartbeat: "심장 박동" } } /// 각 진동이 울리는 시점(초) — 첫 진동은 0 var beatOffsets: [TimeInterval] { switch self { case .once: [0] case .twice: [0, 0.45] case .thrice: [0, 0.45, 0.9] case .heartbeat: [0, 0.3, 1.0, 1.3] } } } /// 세트 구성 — 세트 = (수축 → 이완) × 횟수 nonisolated struct WorkoutConfig: Codable, Equatable { var contractSeconds: Int = 10 var relaxSeconds: Int = 5 var reps: Int = 20 var contractPattern: HapticPattern = .twice var relaxPattern: HapticPattern = .once var totalDuration: TimeInterval { Double(reps * (contractSeconds + relaxSeconds)) } var summaryText: String { "수축 \(contractSeconds)초 · 이완 \(relaxSeconds)초 · \(reps)회" } } @MainActor final class SettingsStore: ObservableObject { static let shared = SettingsStore() @Published var config: WorkoutConfig { didSet { save() if config != oldValue { onConfigChange?(config) } } } /// 폰 → 워치 동기화 훅 (iOS에서만 연결) var onConfigChange: ((WorkoutConfig) -> Void)? private static let key = "workout.config.v1" private init() { if let data = AppGroup.defaults.data(forKey: Self.key), let saved = try? JSONDecoder().decode(WorkoutConfig.self, from: data) { config = saved } else { config = WorkoutConfig() } } private func save() { if let data = try? JSONEncoder().encode(config) { AppGroup.defaults.set(data, forKey: Self.key) } } }