- ActivityKit엔 미래 상태 예약 API가 없음을 확인(푸시 서버 없이는 국면 갱신이 앱 런타임 의존) → 상태에 세트 전체 일정을 실어 시스템 렌더 요소로 심장을 재구성: 국면 색 타임라인 바(하드 스톱 그라데이션) + 실시간 플레이헤드 ProgressView(timerInterval:) + 전체 남은 시간. 플레이헤드가 걸친 색 = 지금 국면 - 국면 라벨·횟수는 앱 갱신 유지(syncCurrent 단일 진입점), stale 시 흐림 - 오디오 키퍼: 완전 무음 대신 ±2 LSB 디더 노이즈(무음 감지 정지 회피) + 백그라운드 전환 ~30초 브리지 태스크 - 통계 타임라인 점별 시각 라벨 제거(겹침 — 시각은 오늘 기록 목록에서) - 도움말·번역 갱신, CLAUDE.md 현행화. 시뮬: DI 백그라운드 80초+(10/20) 진행 확인 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UPmp29DQHY6Ahfss5SRopT
40 lines
1.4 KiB
Swift
40 lines
1.4 KiB
Swift
//
|
||
// KegingActivityAttributes.swift
|
||
// Keging
|
||
//
|
||
// Live Activity(다이나믹 아일랜드) 상태 — 앱·위젯 두 타깃에 동일 파일로 컴파일.
|
||
//
|
||
// ⚠️설계 원칙(실기기 4차 피드백): ActivityKit엔 "미래 시점 상태 예약" API가 없어
|
||
// 국면 전환 갱신은 앱 런타임에 의존한다. 그래서 상태에 세트 전체 일정(sessionStart·
|
||
// 준비/수축/이완/횟수)을 통째로 실어, 위젯이 타임라인 바·진행 플레이헤드·남은 시간을
|
||
// timerInterval 계열(시스템 렌더)로 그리게 한다 — 앱이 정지해도 이 요소들은 계속 움직인다.
|
||
//
|
||
|
||
#if canImport(ActivityKit)
|
||
import ActivityKit
|
||
import Foundation
|
||
|
||
nonisolated struct KegingActivityAttributes: ActivityAttributes {
|
||
nonisolated struct ContentState: Codable, Hashable {
|
||
var phase: KegelPhase
|
||
var rep: Int
|
||
var totalReps: Int
|
||
var phaseStart: Date
|
||
var phaseEnd: Date
|
||
// 자가 렌더링용 전체 일정 — 버튼 누른 시각부터 결정적
|
||
var sessionStart: Date
|
||
var prepSeconds: Int
|
||
var contractSeconds: Int
|
||
var relaxSeconds: Int
|
||
|
||
var sessionEnd: Date {
|
||
sessionStart.addingTimeInterval(
|
||
Double(prepSeconds + totalReps * (contractSeconds + relaxSeconds))
|
||
)
|
||
}
|
||
}
|
||
|
||
var startedAt: Date = Date()
|
||
}
|
||
#endif
|