feat(widget): adopt LiveActivityIntent on the widget run button too
RunActionIntent(위젯 실행 버튼)도 LiveActivityIntent를 채택 — 시스템이 앱 프로세스에서 실행해, 위젯 탭으로 시간 측정을 시작해도 시리 경로와 동일하게 다이나믹 아일랜드가 바로 뜬다. 과거 '위젯 버튼 먹통' 증상 때문에 확장 프로세스 고정을 유지했었으나, 해당 증상은 위젯 재추가·기기 재부팅(캐시)으로 해소된 것으로 확인되어 제약을 풀었다. 실기기에서 응답성 회귀가 보이면 이 인텐트의 LiveActivityIntent 채택 한 줄만 되돌리면 된다 (코드 주석에도 기록). 검증: -intentSmokeTest에 위젯 실행 토글 시나리오 추가(시작 → 토글 종료), 10개 시나리오 전부 통과. Debug·Store 스킴 빌드 성공. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
This commit is contained in:
parent
ec62dc83de
commit
076dad26d8
@ -259,9 +259,9 @@ xcrun xcstringstool sync IOS/Localizable.xcstrings --stringsdata "${files[@]}"
|
||||
|
||||
## 12. 시리 단축어 (`Shared/HaruDanimIntents.swift`, 프리미엄 게이트)
|
||||
|
||||
인텐트: 측정 시작/종료(짧은 기록 무시 규칙 동일 적용, **LiveActivityIntent 채택** — 시리 백그라운드 실행에서도 다이나믹 아일랜드 시작 허용) · 횟수 추가(1~999) · 행동 누적값 조회 · 목표 진행률 조회 · 다짐 진행률 조회(atMost는 한도 문구). `RunActionIntent`(위젯 버튼 전용)는 `isDiscoverable=false`로 단축어 앱에서 숨김이며 확장 프로세스 응답성 유지를 위해 LiveActivityIntent를 채택하지 않는다.
|
||||
인텐트: 측정 시작/종료(짧은 기록 무시 규칙 동일 적용) · 횟수 추가(1~999) · 행동 누적값 조회 · 목표 진행률 조회 · 다짐 진행률 조회(atMost는 한도 문구). 시작/종료와 `RunActionIntent`(위젯 버튼)는 **LiveActivityIntent 채택** — 앱 프로세스에서 실행되어 시리·위젯 탭의 백그라운드 실행에서도 다이나믹 아일랜드가 바로 뜬다 (위젯 버튼 응답성 회귀 시 RunActionIntent의 채택만 되돌릴 것). `RunActionIntent`는 `isDiscoverable=false`로 단축어 앱에서 숨김.
|
||||
- **시리 문구**: `AppShortcutsProvider`에 기본 + 행동 이름 파라미터 문구("하루 다님에서 \(\.$action) 측정 시작" — 되묻지 않고 한 번에 실행). 번역은 **`IOS/AppShortcuts.xcstrings`**(일반 카탈로그와 별개, ko/en/ja). 행동 어휘는 앱 시작 시 `updateAppShortcutParameters()`로 갱신(새 행동은 다음 실행부터 음성 인식).
|
||||
- 엔티티(Action/Goal/Quest)는 EntityStringQuery + '선택 안 함' 항목(위젯 설정용 센티널이 단축어 앱 선택지에도 노출됨 — 알려진 트레이드오프). 도움말 프리미엄 그룹에 사용법 항목, 결제 화면 기능 목록에도 표기. 로직 검증은 `-intentSmokeTest` (§14) — perform() 9개 시나리오를 앱 안에서 실행하고 결과를 Documents/intent-smoke-test.txt로 남긴다.
|
||||
- 엔티티(Action/Goal/Quest)는 EntityStringQuery + '선택 안 함' 항목(위젯 설정용 센티널이 단축어 앱 선택지에도 노출됨 — 알려진 트레이드오프). 도움말 프리미엄 그룹에 사용법 항목, 결제 화면 기능 목록에도 표기. 로직 검증은 `-intentSmokeTest` (§14) — perform() 10개 시나리오를 앱 안에서 실행하고 결과를 Documents/intent-smoke-test.txt로 남긴다.
|
||||
|
||||
---
|
||||
|
||||
@ -289,7 +289,7 @@ xcrun xcstringstool sync IOS/Localizable.xcstrings --stringsdata "${files[@]}"
|
||||
| 모음 | `-startEditing` `-expandGoalCard` `-pinGoals <N>` `-openStatsFor "이름"` `-openHistoryFor "이름"` `-autoStart "이름"` |
|
||||
| 목표 | `-goalShowEditor` `-goalShowFinished` `-goalReorder` `-goalScrollBottom` `-endGoalYesterday "제목"`(종료일을 어제로 — 실행 시점 자동 판정 검증) |
|
||||
| 기록/통계 | `-historyMode timetable` `-historyWeekly` `-excludeActions "이름,이름"` `-historyShowFilter` `-statShowFilter` `-statSpan <day|week|month>` `-statScrollBottom` `-showExport` `-exportDump`(Documents/export-dump.png) |
|
||||
| 설정/기타 | `-settingsScrollGoal` `-settingsScrollPremium` `-premiumPreview` `-helpPreview` `-widgetPreview YES|lock` `-widgetPreviewScroll <앵커>` `cloudSync`(standard bool로 강제) `-intentSmokeTest`(시리 인텐트 9개 시나리오 실행 → Documents/intent-smoke-test.txt, -seedDemo·-premium과 함께) |
|
||||
| 설정/기타 | `-settingsScrollGoal` `-settingsScrollPremium` `-premiumPreview` `-helpPreview` `-widgetPreview YES|lock` `-widgetPreviewScroll <앵커>` `cloudSync`(standard bool로 강제) `-intentSmokeTest`(시리 인텐트 10개 시나리오 실행 → Documents/intent-smoke-test.txt, -seedDemo·-premium과 함께) |
|
||||
| 버블 | `-radialExpanded` |
|
||||
| 일기 | `-diarySeed` `-diaryOpenToday`(**`-startTab diary` 필수**) `-diaryPage <N>` `-diaryShowConfig` `-diaryShowExport`(달력 화면 onAppear — `-diaryOpenToday`와 함께 쓰면 안 뜸) `-diaryExportRun pdf|image`(결과를 Documents로 복사) `-diaryShowCalendarPicker` `-diarySeedEvents` `-diaryHideFirstAction` `-diaryShowActionFilter` / 섹션 강제: `-diary.sectionOrder "timetable,hero,goals"` `-diary.hiddenSections "mood,todos,records,bars"` |
|
||||
| 워치 | `-complicationPreview` `-complicationScroll` `-autoRunFirstAction` |
|
||||
|
||||
@ -107,6 +107,15 @@ enum DebugSeed {
|
||||
log("타입 방어", "실패 — 시간형에 횟수 추가가 통과됨")
|
||||
} catch { log("타입 방어", "OK — 거부: \(String(localized: error.asIntentMessage))") }
|
||||
|
||||
// ⑤-2 위젯 실행 버튼(RunActionIntent): 시간형 시작 → 다시 실행하면 종료 토글
|
||||
do {
|
||||
_ = try await RunActionIntent(actionID: running.id.uuidString).perform()
|
||||
let startedOK = try IntentStore.action(running.id).isRunning
|
||||
_ = try await RunActionIntent(actionID: running.id.uuidString).perform()
|
||||
let stoppedOK = try !IntentStore.action(running.id).isRunning
|
||||
log("위젯 실행 토글", startedOK && stoppedOK ? "OK — 시작 후 토글 종료" : "실패 — 시작=\(startedOK) 종료=\(stoppedOK)")
|
||||
} catch { log("위젯 실행 토글", "throw: \(String(localized: error.asIntentMessage))") }
|
||||
|
||||
// ⑥ 조회 인텐트 3종 — throw 없이 완료되는지
|
||||
do {
|
||||
let total = ActionTotalIntent()
|
||||
|
||||
@ -379,7 +379,10 @@ struct QuestEntityQuery: EntityStringQuery {
|
||||
// MARK: - 인텐트: 행동 실행 (위젯 버튼용 토글)
|
||||
|
||||
/// 인터랙티브 위젯의 실행 버튼: 시간형은 시작/종료 토글, 횟수형은 +1
|
||||
struct RunActionIntent: AppIntent {
|
||||
/// LiveActivityIntent 채택: 시스템이 앱 프로세스에서 실행해 위젯 탭으로 측정을 시작해도
|
||||
/// 다이나믹 아일랜드가 바로 뜬다. (과거 위젯 버튼 먹통 증상은 위젯 캐시/재부팅으로 해소된
|
||||
/// 것으로 확인되어 확장 프로세스 고정 제약을 풀었다 — 실기기에서 응답성 회귀 시 이 채택만 되돌릴 것)
|
||||
struct RunActionIntent: LiveActivityIntent {
|
||||
static let title: LocalizedStringResource = "행동 실행"
|
||||
static let description = IntentDescription("시간 측정 행동은 시작/종료를 전환하고, 횟수 기록 행동은 횟수를 1 추가해요.")
|
||||
|
||||
@ -441,8 +444,7 @@ enum ActionRunner {
|
||||
// MARK: - 인텐트: 시간 측정 시작
|
||||
|
||||
/// LiveActivityIntent 채택: 시리/단축어의 백그라운드 실행에서도 Live Activity 시작이
|
||||
/// 허용되어, 앱을 열지 않아도 다이나믹 아일랜드에 측정이 표시된다. (앱 프로세스에서 실행됨 —
|
||||
/// 위젯 버튼이 쓰는 RunActionIntent는 확장 프로세스 응답성 튜닝을 유지하려 채택하지 않는다)
|
||||
/// 허용되어, 앱을 열지 않아도 다이나믹 아일랜드에 측정이 표시된다. (앱 프로세스에서 실행됨)
|
||||
struct StartTimeActionIntent: LiveActivityIntent {
|
||||
static let title: LocalizedStringResource = "시간 측정 시작"
|
||||
static let description = IntentDescription("시간 측정 행동의 측정을 시작해요.")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user