Conversation
- WatchEnvelope 봉투 코덱 추가 — JSON 을 단일 키에 Data 로 실어 sendMessage · updateApplicationContext · transferUserInfo 세 채널이 하나의 코덱을 공유 - WatchMessage(요청 5종) · WatchReply(응답 4종) · WatchConnectivityError(실패 11종) 계약 정의, 수동 Codable 로 스키마 버전 상한 검증 - 페이로드 값 타입 추가 — WatchSessionState · WatchSchedule · WatchNotice · WatchAttendanceRequest / Result. 서버 정수 식별자는 전 레이어 String (절대 규칙 #2) - WatchSessionCoordinator 를 @mainactor @observable 로 재작성, WCSessionDelegate 양방향 수신 경로 구현 (요청 핸들러 주입 · userInfo AsyncStream · 앱 컨텍스트 상태) - replyHandler 없이 sendMessage 를 호출해 성공 시 continuation 이 매달리던 버그를 구조적으로 제거 — 모든 전송이 replyHandler 를 넘긴다 - WatchMessenger 삭제 — 구현체 1개짜리 pass-through 프로토콜이었고 Sendable 준수가 non-Sendable 코디네이터와 충돌 - CoreWatchConnectivity 에 테스트 타깃 활성화 (includesTests: true) - 계약 테스트 22개 추가 (2 suites)
- Core/WatchDesignSystem 모듈 신설 — iOS 카탈로그는 watchOS 에서 항상 dark 를 resolve 하므로(브랜드색이 #4869F0 대신 #4264F0 이 됨) 워치 전용 레이어로 분리 - 토큰 3종 추가: WatchColor(표면·브랜드·상태·텍스트 20종) · WatchTypography(WatchTextRole 5종) · WatchLayout(코너·보더·패딩·스페이싱) - 표면 API 추가: watchCard(_:leadingAccent:) · watchScreenBackground() · watchListRowBackground(isSelected:) - 컴포넌트 2종 추가: WatchActionButton(역할 3종) · WatchStatusBadge(상태 5종) - Glass 절제 규칙을 API 로 강제 — WatchCardStyle 을 닫힌 enum 으로 두어 카드에 Glass 를 넣을 경로 자체를 없애고, Glass API 는 WatchActionButton 한 파일로 한정 - 토큰 드리프트 가드 테스트 추가 — iOS Colors.xcassets 를 파싱해 브랜드색 불일치 시 실패 - docs/claude/watch-design-system.md 신규 · CLAUDE.md 레퍼런스 인덱스 갱신
- WatchRoute/WatchFallbackRoute — 출석·The Ping 두 축 진입점과 폴백 3종(권한 거부·연결 끊김·큐잉)을 경로로 확보 - WatchRouter — 앱 셸이 소유하는 @observable 단일 스택. NavigationPath 대신 [WatchRoute] 구체 배열로 두어 스택 내용을 테스트로 검증 - WatchRootView — navigationDestination 단일 등록. 목적지는 후속 이슈가 교체할 플레이스홀더 - HomeGlanceView/HomeGlanceViewModel — solid 카드 글랜스, 세션·공지 행을 tap-chip 으로 승격, 세션 없을 때 "오늘 세션 없음" 빈 상태 - 승인 대기는 WatchStatus.pending(중립+인디고 링). 앰버 #FFB340 은 개정 스펙상 지각 전용이라 쓰지 않음 - 카운트·식별자는 전 레이어 String, Int 변환은 비교 시점에만 (규칙 #2). 숫자 아닌 값은 0건 처리 - watchAppProject 헬퍼에 includesTests 추가 + WKApplication 키 보강 — 없으면 테스트 호스트 설치가 거부됨 - ContentView("Hello Watch") 제거
- "오늘 세션" 라벨 + "오늘 세션 없음" 값이 같은 말을 두 줄로 반복해 위계가 흐려지던 문제 - 빈 상태는 문구 한 줄만 카드에 남긴다. 세션이 있을 때의 라벨/값 위계는 그대로
- WatchPingItem: 스냅샷 공지 + 낙관적 읽음 오버레이를 합쳐 최신순 목록을 만드는 표시 모델. 안읽음·긴급·필수 확인 세 신호를 색이 아닌 글자로도 푸는 VoiceOver 라벨 포함 - PingInbox: 앱 셸이 소유하는 수신함. applicationContext 수신분과 requestSync 결과 중 더 새로운 스냅샷을 고르고, 실패해도 캐시를 버리지 않아 iPhone 이 멀어져도 마지막 공지를 읽을 수 있다 - 확인 전송은 transferUserInfo 큐 채널(.noticeRead) — 연결이 끊겨 있어도 접수되고 앱 종료 후에도 이어진다. 전송 즉시 읽음을 오버레이해 목록·상세가 같은 상태를 본다 - WatchConnectivityError → AppError 매핑: 스키마 불일치는 손상과 구분해 업데이트 안내로 낸다 - PingListView / PingDetailView 를 WatchRoute 의 .pingList · .pingDetail 목적지에 연결 - watchListRowBackground 에 leadingAccent 추가 — 긴급 색바를 행 배경이 그려 행 끝까지 닿게 한다 - 테스트 21건 추가 (WCSession 없이 정렬·오버레이·스냅샷 선택·에러 매핑 검증)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🔗 관련 이슈
Closes #1208
선행 PR 위에 쌓았습니다 — 아래가 먼저 머지돼야 합니다.
✨ PR 유형
새로운 기능 (워치 앱 화면 추가)
📷 스크린샷 or 영상(UI 변경 시)
시뮬레이터 캡처는 아직 첨부하지 못했습니다 — iPhone 컴패니언이 스냅샷을 밀어 주는 경로(#1210 의 iPhone 측 퍼블리셔)가 아직 붙지 않아, 워치 단독 실행 시 실제 공지 데이터가 들어오지 않습니다.
대신
#if DEBUG프리뷰 6종으로 상태별 화면을 모두 확인할 수 있습니다.PingListView — 미확인·긴급·확인함PingListView — 빈 상태PingListView — A11y 크기PingDetailView — 미확인 긴급PingDetailView — 확인 완료PingDetailView — A11y 크기🛠️ 작업내용
표시 모델 —
WatchPingItemWatchNotice와 워치에서 방금 누른 낙관적 읽음 오버레이를 합쳐 최신순 목록을 만듭니다.isAlert) · 필수 확인(isMustRead) 세 신호를 서로 다른 축으로 유지합니다. 확인한 긴급 공지는 긴급 신호만 남습니다.accessibilityLabel)을 모델이 직접 만듭니다.상태 —
PingInbox(@Observable,Loadable)WatchRouter와 같은 자리). 확인 버튼은 상세에서 눌리고 결과는 목록이 즉시 반영해야 해서, 화면 로컬 상태로는 이 왕복이 성립하지 않습니다.WatchSessionCoordinator.receivedState(applicationContext 수신분)와requestSync()결과를 각각 들고,generatedAt이 더 새로운 쪽을 화면에 그립니다. 동률이면 iPhone 이 퍼블리시한 컨텍스트를 택합니다.WatchConnectivityError→AppError매핑. 스키마 불일치는 페이로드 손상과 구분해 「iPhone 앱을 업데이트해 주세요」로 냅니다.확인 전송 (양방향 동기화)
transferUserInfo큐 채널(.noticeRead)로 보냅니다 — 계약상 이 종류가 허용하는 유일한 채널이고, 연결이 끊겨 있어도 접수되며 앱이 종료돼도 시스템이 전송을 이어 갑니다.화면
PingListView—List+Loadable분기(로딩 / 빈 상태 / 목록 / 실패). 행 배경은watchListRowBackground로 불투명 서피스만 씁니다(Glass 미적용, 스펙 §2.2).PingDetailView— 본문ScrollView+.safeAreaInset(edge: .bottom)하단 고정 확인 CTA. 확인 완료 시 비활성 + 사유 표시.WatchRootView의navigationDestination에.pingList·.pingDetail(noticeID:)를 연결했습니다.WatchRoute케이스는 추가하지 않았습니다(✨ Feature: Watch 홈 통합 글랜스 + 네비게이션 IA 골격 #1206 이 정의한 열거형을 그대로 씁니다).디자인 시스템
watchListRowBackground(isSelected:leadingAccent:)에leadingAccent를 추가했습니다. 긴급 색바를 행 배경이 그려야 행 끝까지 닿습니다 — 콘텐츠에 그리면 셀 인셋만큼 떠서 시안과 어긋납니다.의존성
UMCWatchApp에UMCFoundation을 추가했습니다.Loadable·AppError를 워치 전용으로 다시 만들지 않기 위해서이며, 해당 모듈은 이미destinations: [.iPhone, .appleWatch]로 워치 슬라이스를 빌드합니다.테스트 21건 추가 —
WCSession없이 검증되도록 정렬·오버레이·스냅샷 선택·에러 매핑을 순수 함수로 분리했습니다.📋 추후 진행 상황
updateApplicationContext로 밀어 주는 경로와, 워치가 보낸.noticeRead를 서버 수신 확인 API 로 중계하는 핸들러는 ♻️ Refactor: WatchConnectivity 도메인 페이로드 계약 정의 + 양방향 수신 경로 구현 #1210 iPhone 측 후속 작업입니다. 이 PR 은 워치 쪽 소비·전송만 담당합니다.WatchColor.statusError(#FF453A) 를 썼습니다. 시안에 워치 전용 긴급 색이 따로 있다면 토큰을 교체해야 합니다.📌 리뷰 포인트
PingInbox.latest(_:_:).requestSync()는coordinator.receivedState를 갱신하지 않아 출처가 둘로 갈립니다.generatedAt비교 + 동률 시 컨텍스트 우선이 맞는 규칙인지 봐 주세요.transferUserInfo는 시스템이 전달을 보장하므로 롤백 경로를 두지 않았습니다.List행·화면 배경에 Glass 를 쓰지 않았습니다(워치 스펙 §2.2). 누락된 곳이 있는지 봐 주세요.✅ Checklist
PR이 다음 요구 사항을 충족하는지 확인해주세요!!!
🧪 검증
xcodebuild test -scheme UMCWatchApp -destination 'platform=watchOS Simulator,name=Apple Watch Series 11 (46mm)'cd UMCApp && make test