mycode/myApp/Keging/Shared/WorkoutSettings.swift
songyc macbook 43448b35f3 feat(keging): 1.0 전 기능 구현 — iOS 타이머·진동 패턴·통계·다이나믹 아일랜드·단축어·워치 앱
- 세트 엔진(수축→이완×횟수, 중단=미기록) iOS·워치 공용, 완주만 records.json 저장
- 첫 화면 시작 버튼 + 세팅(시간·횟수·진동 패턴)/설정(테마) 시트, 아래 스와이프 통계
- 통계: 하루(시간대별)/주간/월간 꺾은선, 달력 기준↔롤링(지난 7일·30일) 전환
- Live Activity(수축/이완·n/총·카운트다운), 백그라운드 무음 오디오로 타이머·진동 유지
- 단축어 '케겔 운동 시작', 워치 앱(즉시 시작·워치 전용 진동·physical-therapy 세션·기록 폰 병합)
- 하루 다님 팔레트 계승(라이트/다크/시스템), 앱 아이콘 SVG→PNG 등록
- 검증: Debug·Release·워치 빌드 그린, 시뮬 QA 10장(26·18.5·워치 46/40mm), 완주·기록 경로 확인

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UPmp29DQHY6Ahfss5SRopT
2026-08-29 10:08:20 +09:00

81 lines
2.2 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.

//
// 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)
}
}
}