--- id: ios-watchos-patterns title: watchOS — Watch app / 복잡도 제한 / 통신 category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [ios, watchos, vibe-coding] tech_stack: { language: "Swift / SwiftUI / WatchConnectivity", applicable_to: ["watchOS"] } applied_in: [] aliases: [watchOS, Apple Watch, complications, WatchConnectivity, WCSession] --- # watchOS > 단순 + 빠른. **30초 이상 화면 X**. SwiftUI 표준. iPhone ↔ Watch 통신 = WatchConnectivity. Complications + smart stack 이 진짜 가치. ## 📖 핵심 개념 - App: short interaction (10-30초). - Complication: 시계 face 의 작은 데이터. - Smart Stack: 위젯처럼 timeline. - Connectivity: iPhone 과 데이터 공유. ## 💻 코드 패턴 ### Watch app 구조 ```swift @main struct WatchApp: App { var body: some Scene { WindowGroup { NavigationStack { HomeView() } } } } struct HomeView: View { @StateObject var vm = HomeViewModel() var body: some View { List(vm.items) { item in NavigationLink(value: item) { HStack { Image(systemName: "clock"); Text(item.name) } } } .navigationDestination(for: Item.self) { ItemDetail($0) } } } ``` ### Complication (WidgetKit, watchOS 9+) ```swift struct WatchComplication: Widget { var body: some WidgetConfiguration { StaticConfiguration(kind: "step", provider: StepProvider()) { entry in Text("\(entry.steps)") .containerBackground(.fill.tertiary, for: .widget) } .supportedFamilies([ .accessoryCircular, .accessoryCorner, .accessoryInline, .accessoryRectangular, ]) } } ``` ```swift struct StepEntry: TimelineEntry { let date: Date let steps: Int } struct StepProvider: TimelineProvider { func placeholder(in context: Context) -> StepEntry { StepEntry(date: Date(), steps: 0) } func getSnapshot(in context: Context, completion: @escaping (StepEntry) -> Void) { ... } func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { // 시간별 entries } } ``` ### WatchConnectivity (iPhone ↔ Watch) ```swift import WatchConnectivity class WCManager: NSObject, ObservableObject, WCSessionDelegate { static let shared = WCManager() override init() { super.init() if WCSession.isSupported() { WCSession.default.delegate = self WCSession.default.activate() } } func session(_ session: WCSession, activationDidCompleteWith state: WCSessionActivationState, error: Error?) {} // Receiving func session(_ session: WCSession, didReceiveMessage message: [String: Any]) { // 즉시 처리 } func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String: Any]) { // 백그라운드 sync 가능 } } // 보내기 WCSession.default.sendMessage(["action": "fetch"], replyHandler: { reply in // 즉시 응답 }, errorHandler: { e in print(e) }) // 또는 application context (latest snapshot) try WCSession.default.updateApplicationContext(["count": 42]) ``` ### Workout / HealthKit ```swift import HealthKit let store = HKHealthStore() let config = HKWorkoutConfiguration() config.activityType = .running config.locationType = .outdoor let session = try HKWorkoutSession(healthStore: store, configuration: config) let builder = session.associatedWorkoutBuilder() session.startActivity(with: Date()) // ... data collection session.end() try await builder.endCollection(at: Date()) let workout = try await builder.finishWorkout() ``` ### Always-On Display (AOD) ```swift @Environment(\.scenePhase) var scenePhase // scenePhase = .background → AOD // 절제된 정보만 (핵심 숫자) .scenePhaseAware { phase in if phase == .background { // dim, 단순화 } } ``` ### Crown rotation ```swift @State var value: Double = 0 ScrollView { Text("\(value)").focusable() .digitalCrownRotation($value, from: 0, through: 100, by: 1, sensitivity: .medium, isContinuous: false) } ``` ### Notification (rich) ```swift class NotificationController: WKUserNotificationHostingController { override var body: NotificationView { NotificationView() } } ``` ## 🤔 의사결정 기준 | 상황 | 권장 | |---|---| | 단순 viewer | Complication + Smart Stack | | 짧은 입력 | 음성 / digital crown | | 운동 추적 | HealthKit + WorkoutSession | | iPhone 의존 데이터 | WatchConnectivity + applicationContext | | 대용량 동기화 | transferUserInfo / file | | 알림 풍부 | Custom notification view | ## ❌ 안티패턴 - **iOS UI 직접**: 화면 작음 — 단순화. - **30초 이상 작업**: 화면 sleep. background task. - **Always-on 무절제 데이터**: 배터리. dim mode. - **WCSession.sendMessage 큼**: 작은 ping 만. context 사용. - **Complication 매분 갱신**: 배터리. 시간별. - **Health permission 미설명**: deny 자주. - **Crown 무시**: 핵심 입력. 활용. ## 🤖 LLM 활용 힌트 - SwiftUI + WidgetKit (Complication) + WCSession 3종. - HealthKit 표준. - 짧고 단순 + Always-On 고려. ## 🔗 관련 문서 - [[iOS_SwiftUI_State_Property_Wrappers]] - [[iOS_Widget_Extension]] - [[iOS_Background_Tasks]]