4 Commits

Author SHA1 Message Date
songyc macbook
32306510c8 fix(widget): make interactive AppIntent writes reliable and crash-proof
Interactive home/lock-screen widget buttons (count +1, timer start/stop)
gave touch feedback but often didn't persist, only working after several
rapid taps. Two compounding defects in the widget-extension write path:

- The extension could crash mid-write. refresh()/makeContainer() opens a
  new ModelContainer on every intent perform and every timeline read; in
  the extension that hit a local-store branch whose failure was fatalError.
  A transient App Group store contention with the running main app crashed
  the whole appex — the tap looked dead and only a later retry won the race.
  Split makeContainer into a throwing makeContainerThrowing(); refresh() now
  wraps it in try? and keeps the previous container on failure, so the
  extension degrades instead of crashing. The app's DataStore.shared keeps
  its crash-on-failure semantics.

- Fetch → mutate → save could span different contexts and save errors were
  swallowed. IntentStore.context is a computed container.mainContext, and
  commit() re-read it for a try? save(), so a mid-flight container swap left
  the mutation on a dead context and any save failure vanished silently.
  Added IntentStore.performWrite(_:) which captures one context for the whole
  fetch+mutate+save, saves with a retry (no silent try?), then fires the
  Live Activity hook and reloadAllTimelines. All four mutating intents
  (RunAction/StartTime/StopTime/AddCount) now fetch in that single context.

No @Model or main-app logic changed — fix is confined to the widget/intent
data layer. Verified: 5 rapid RunActionIntent.perform() calls persist +5 with
no lost writes across three cold launches (ok=true each). Build succeeds
(app + embedded Haru_DanimWidgets.appex).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-12 11:01:47 +09:00
songyc macbook
fbd4810628 fix: resolve widget sync crashes via local AppGroup container and improve goal widget to show all sub-goals
위젯 동기화 크래시 수정:
- DataStore: CloudKit 미러링을 메인 앱 프로세스 전담으로 분리.
  위젯 등 앱 확장(.appex)은 CloudKit 엔타이틀먼트가 없고 메모리·수명
  제약으로 미러링 설정이 크래시/행을 유발하므로, 확장 프로세스는 항상
  같은 App Group 스토어 파일을 로컬 전용(cloudKitDatabase: .none)으로
  연다. 확장이 쓴 변경은 메인 앱이 히스토리로 집어 CloudKit에 내보냄
- CloudSyncMonitor 신설: 메인 앱이 NSPersistentStoreRemoteChange
  (다른 기기의 CloudKit 가져오기·위젯 인텐트의 쓰기)를 디바운스로 받아
  라이브 액티비티 → 위젯 타임라인 → 워치 스냅숏 순으로 즉시 갱신
- 인터랙티브 위젯(AppIntent)의 컨테이너 매핑 점검: IntentStore.commit이
  저장 직후 reloadAllTimelines를 이미 보장함을 확인·문서화

모음 탭 목표 진행 현황 카드 개선:
- '다짐별로 보기'의 상위 3개 제한 제거 — 모든 다짐 확인 가능
- 화면 균형을 위해 접힘/펼침 UX 적용: iPhone은 3개, iPad는 6개까지
  접힌 상태로 보여주고 '다짐 N개 더 보기' 버튼으로 카드 안에서 펼침
  (카드 탭 이동과 충돌하지 않는 독립 버튼, 스냅 애니메이션)
- DebugSeed: 접힘/펼침 검증용 4번째 다짐 추가, -expandGoalCard 인자 추가

검증: iPhone 17 Pro·iPad Pro 11 시뮬레이터에서 접힘/펼침 렌더링과
사이드바 레이아웃 확인, 전체 타깃(iOS·워치·위젯) 빌드 성공

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-11 21:35:20 +09:00
songyc macbook
9bf617f3e5 feat(widgets): 위젯 테마 옵션 추가 — 앱 테마 일치/라이트·다크 고정/리퀴드 글라스 (CLAUDE.md §8.1)
- 홈 위젯 5종(행동 실행/목표 진행률/다짐 진행률/다짐 현황/통계) 설정에
  '위젯 테마' 옵션 추가
- 앱 테마와 일치: 앱의 라이트/다크 설정을 따름 (테마 키를 App Group으로 이관해
  위젯이 읽을 수 있게 함)
- 라이트/다크 고정: 컬러 스킴 강제 + 해당 모드 팔레트
- 리퀴드 글라스: 반투명 머티리얼 배경 + 표면 셀도 머티리얼로 변형해
  유리 질감 위에서 가독성 유지
- DEBUG 미리보기가 테마를 재현하도록 개선 (-widgetTheme light|dark|glass,
  글라스는 그라데이션 배경 위에 렌더링)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 09:14:31 +09:00
songyc macbook
f468fdf296 feat(data): SwiftData 저장소를 App Group으로 이관하고 CloudKit 동기화 지원
- DataStore: App Group 컨테이너의 HaruDanim.store 사용, 기존 default.store 1회 자동 이관
- 프리미엄 + 동기화 토글 시 CloudKit(.private) 구성으로 컨테이너 생성, 실패하면 로컬 폴백
- CloudKit 요건(모든 관계 optional)에 맞춰 to-many 관계를 optional 저장 + 기존 이름
  접근자로 리팩터링 (originalName 매핑으로 기존 데이터 유지)
- Tag/Action/Goal/Quest에 uuid 추가 (시리 단축어·위젯·워치 식별자) + 중복 보정
- 모델 레이어(Models/Settings/DayMath/QuestProgress/Formatters/Theme)를 Shared/로 이동
  — 위젯 확장과 공유
- 집계 설정(주 시작 요일·하루 시작 시간·대표 시간·짧은 기록 무시)을 App Group defaults로
  이관해 위젯·워치와 공유, 설정의 '기기 간 동기화' 토글 활성화
- Goal.combinedSpanRatio 공용화 (모음 탭 카드·위젯·시리가 동일 산식 사용)
- DEBUG 검증 인자: -cloudSync YES, -settingsScrollPremium YES

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 08:59:15 +09:00