fix(watch): list every goal & quest in complication recommendations
- 페이스 편집기에 3번째 목표부터(다짐은 첫 목표의 2개까지만) 안 뜨던 원인 = recommendations()의 prefix(2) 하드캡 — 워치는 컴플리케이션 파라미터 편집 UI가 없어 추천 목록이 곧 선택지의 전부라, 캡에 걸린 목표·다짐은 페이스에 추가할 방법 자체가 없었음 (보고된 13개 = 현재현황 1 + 목표 2×3기간 + 다짐 2×3기간과 정확히 일치) - 목표·다짐 달성률 둘 다 상한 제거: 진행 중 목표 전부 × 3기간, 다짐 전부 × 3기간 나열 - 워치 앱이 스냅숏을 적용할 때 invalidateConfigurationRecommendations() 호출 추가 — 이후 아이폰에서 목표를 새로 만들어도 시스템 임의 재조회를 기다리지 않고 목록에 자동 반영 - CLAUDE.md §11에 "추천 목록 = 선택지 전부, 상한 금지" 규칙 명시 - 검증: 워치·Debug·Store 3스킴 빌드 성공, 워치 심 -complicationPreview 렌더 정상 (추천 목록 자체는 페이스 편집 UI라 헤드리스 확인 불가 — 실기기 확인 필요) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
This commit is contained in:
parent
fef25f30d0
commit
232e8c504d
@ -282,6 +282,7 @@ xcrun xcstringstool sync Widgets/Localizable.xcstrings --stringsdata "${widget_f
|
||||
- 워치→iPhone: `sendMessage`(응답=최신 스냅숏), 실패/미활성 시 `transferUserInfo` 큐 폴백 (5분 지난 큐 명령은 무시)
|
||||
- 스냅숏은 워치 App Group defaults에 캐시(오래된 스냅숏 역행 방지: generatedAt 비교) → 컴플리케이션이 읽음
|
||||
- **컴플리케이션** 3종 (15분 .after 타임라인): 현재 현황(대표 행동+타이머+N) / 목표 달성률(기간 선택, 게이지) / 다짐 달성률(rectangular는 3기간 모두, **atMost는 게이지 대신 한도 안/초과 상태 표현**)
|
||||
- **추천 목록(recommendations) = 선택지 전부**: 워치는 컴플리케이션 파라미터 편집 UI가 없어 페이스 편집기의 목록이 recommendations()가 돌려준 조합으로 한정된다 — 진행 중 **목표 전부 × 3기간, 다짐 전부 × 3기간**을 상한 없이 나열할 것(예전 prefix(2) 캡이 "3번째 목표부터 페이스에 추가 불가" 버그였음). 워치 앱이 스냅숏을 적용할 때 `invalidateConfigurationRecommendations()`를 호출해 목표·다짐 변경이 목록에 자동 반영된다
|
||||
- **유령 타이머 안전장치**(현재 현황): '측정 중' 스냅숏이 `generatedAt` 기준 2시간을 넘기면 무한 타이머 대신 "새로고침 필요" 표시로 강등(`StatusProvider.runningTrustInterval`) — 종료 푸시가 유실돼도 유령 타이머가 몇 시간씩 남지 않음. 워치 앱을 열면 즉시 복구, 진짜 장시간 측정도 다음 스냅숏에 타이머 복귀(데이터 무영향)
|
||||
- ⚠️ `recommendations()`의 description에 포맷 텍스트 금지 — `Text(verbatim:)`만 (WidgetKit assertion으로 익스텐션 즉사)
|
||||
|
||||
|
||||
@ -100,6 +100,9 @@ final class WatchStore: NSObject {
|
||||
Self.groupDefaults.set(data, forKey: WatchSync.cachedSnapshotKey)
|
||||
}
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
// 목표·다짐이 바뀌었을 수 있으니 페이스 편집기의 컴플리케이션 추천 목록도 다시 만든다
|
||||
// (이 호출이 없으면 새로 만든 목표가 시스템이 임의로 재조회할 때까지 목록에 안 뜬다)
|
||||
WidgetCenter.shared.invalidateConfigurationRecommendations()
|
||||
#if DEBUG
|
||||
autoRunIfNeeded()
|
||||
#endif
|
||||
|
||||
@ -69,11 +69,14 @@ struct GoalRateProvider: AppIntentTimelineProvider {
|
||||
}
|
||||
|
||||
/// 워치 페이스 갤러리에 노출할 추천 구성 (목표 × 하루/주간/월간)
|
||||
/// 워치는 컴플리케이션 파라미터 편집 UI가 없어 **이 추천 목록이 곧 선택지의 전부**다 —
|
||||
/// 개수 상한을 두면 그 뒤 목표는 페이스에 추가할 방법이 없다
|
||||
/// (예전 prefix(2)가 "3번째 목표부터 목록에 안 뜨는" 버그의 원인). 진행 중 목표 전부를 나열한다.
|
||||
/// 주의: 추천 description에 문자열 보간(포맷 텍스트)을 쓰면 WidgetKit assertion으로
|
||||
/// 익스텐션이 즉사해 컴플리케이션이 페이스 편집 목록에 아예 나타나지 않는다.
|
||||
/// 반드시 Text(verbatim:)처럼 포맷 없는 텍스트를 넘길 것.
|
||||
func recommendations() -> [AppIntentRecommendation<GoalRateConfigIntent>] {
|
||||
let cached = (ComplicationStore.snapshot()?.goals ?? []).prefix(2)
|
||||
let cached = (ComplicationStore.snapshot()?.goals ?? [])
|
||||
.map { WatchGoalEntity(id: $0.id, title: $0.title) }
|
||||
// 스냅숏이 아직 없으면 자리 표시용 엔티티로 추천 (표시 시점엔 첫 목표로 대체됨)
|
||||
let goals = cached.isEmpty
|
||||
@ -212,14 +215,15 @@ struct QuestRateProvider: AppIntentTimelineProvider {
|
||||
}
|
||||
|
||||
/// 워치 페이스 갤러리에 노출할 추천 구성 (다짐 × 하루/주간/월간)
|
||||
/// 추천 목록이 곧 선택지의 전부이므로 상한 없이 전부 나열한다 (GoalRate 쪽 주석 참고 —
|
||||
/// 예전 prefix(2)가 "첫 목표의 두 다짐까지만 뜨는" 버그의 원인).
|
||||
/// 주의: description에 문자열 보간 금지 — Text(verbatim:) 사용 (GoalRate 쪽 주석 참고)
|
||||
func recommendations() -> [AppIntentRecommendation<QuestRateConfigIntent>] {
|
||||
let cached = (ComplicationStore.snapshot()?.goals ?? [])
|
||||
.flatMap { goal in goal.quests.map { WatchQuestEntity(id: $0.id, title: "\(goal.title) · \($0.name)") } }
|
||||
.prefix(2)
|
||||
let quests = cached.isEmpty
|
||||
? [WatchQuestEntity(id: UUID(), title: String(localized: "다짐"))]
|
||||
: Array(cached)
|
||||
: cached
|
||||
return quests.flatMap { quest in
|
||||
WatchSpanOption.allCases.map { span in
|
||||
let intent = QuestRateConfigIntent()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user