Upgrade watch complications to WidgetKit#5027
Conversation
- Resize Logo asset to 120px so WidgetKit can archive the complication view (1024px hit the imageTooLarge limit and left the complication blank) - Add the Assist custom symbol (message-processing-outline) to the widget catalog - Render the Assist symbol for the Assist complication and the template logo for the Home Assistant / placeholder complication - Use bundled asset images for the AppIntents configuration picker tiles Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Assist complication's `homeassistant://assist` widget URL was never received: the watch app ran on the legacy WatchKit lifecycle (@main on ExtensionDelegate + WKHostingController), where SwiftUI's .onOpenURL / .onContinueUserActivity are inert (runtime warned "Cannot use Scene methods for URL … without SwiftUI Lifecycle"). - Adopt the SwiftUI App lifecycle: @main HomeAssistantWatchApp with WKApplicationDelegateAdaptor(ExtensionDelegate.self) and WindowGroup { WatchHomeView() } - WatchHomeView now handles the assist deep link via .onOpenURL / .onContinueUserActivity, plus a cold-launch pending flag consumed on appear - Replace the Assist fullScreenCover fatalError with a "configure Assist" message Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… limit The ExtensionDelegate writer rasterized complication icons at 96pt (=192px @2x) and shipped the placeholder's RoundLogo at 1024px into the app-group payload. WidgetKit caps complication images at ~122px (measured in pixels), so archiving failed and every user complication — including gauge/ring ones — rendered empty. - Render user-complication icons at 56pt (=112px @2x), under the limit - Drop the placeholder/Assist raster icons from the payload (iconData = nil); the widget extension already renders its bundled, correctly-sized Logo and Assist symbol Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR migrates the Apple Watch complications from the legacy ClockKit-based complication extension to a watchOS 10+ WidgetKit-based implementation, while also expanding the watch app’s offline/sync/editing capabilities (config editing on-watch, chunked database mirroring, improved Assist launch handling, and additional troubleshooting actions).
Changes:
- Adds a new watchOS WidgetKit extension (“WatchWidgets”) with AppIntent-based configuration and multiple accessory widget families.
- Updates the watch app to better support complication/widget deep links (notably Assist), adds on-watch configuration/edit flows, and improves troubleshooting (local wipe, client event log UI).
- Improves watch ↔ iPhone sync robustness via guaranteed-message fallback for config pulls and chunked streaming for the watch database mirror.
Reviewed changes
Copilot reviewed 60 out of 100 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| WatchWidgets/WatchWidgetsEntryView.swift | SwiftUI rendering for WidgetKit entries across supported families. |
| WatchWidgets/WatchWidgets.swift | Widget bundle + AppIntent configuration for the watch widgets extension. |
| WatchWidgets/WatchWidgetLogoView.swift | Shared logo view helper for widget rendering. |
| WatchWidgets/WatchWidgetEntry.swift | Timeline entry model for the watch widget. |
| WatchWidgets/WatchWidgetConstants.swift | Constants for kind/app group, deep links, layout sizes, etc. |
| WatchWidgets/WatchWidgetComplicationSnapshotStore.swift | Reads stored complication “snapshot” recommendations for widgets. |
| WatchWidgets/WatchWidgetComplicationSnapshot.swift | Codable snapshot model + tint/icon helpers for widget rendering. |
| WatchWidgets/WatchWidgetComplicationEntity.swift | AppIntents entity/query types for widget configuration picker. |
| WatchWidgets/WatchWidgetAppIntent.swift | WidgetConfigurationIntent + AppIntentTimelineProvider implementation. |
| WatchWidgets/WatchComplicationFamily.swift | Mapping from WidgetKit families → preferred legacy complication families. |
| WatchWidgets/Info.plist | Declares WidgetKit extension point identifier. |
| WatchWidgets/Color+Hex.swift | Local Color hex parsing + HA brand color for widget extension. |
| WatchWidgets/Assets.xcassets/WidgetBackground.colorset/Contents.json | Widget background color asset metadata. |
| WatchWidgets/Assets.xcassets/TemplateLogo.imageset/Contents.json | Template-rendered logo asset metadata for widgets. |
| WatchWidgets/Assets.xcassets/message-processing-outline.symbolset/haassist.symbols.svg | Assist symbol asset (SVG) for widget rendering. |
| WatchWidgets/Assets.xcassets/message-processing-outline.symbolset/Contents.json | Symbol set catalog metadata. |
| WatchWidgets/Assets.xcassets/Logo.imageset/Contents.json | Logo asset metadata for watch widgets. |
| WatchWidgets/Assets.xcassets/Contents.json | Asset catalog root metadata for watch widgets. |
| WatchWidgets/Assets.xcassets/AppIcon.appiconset/Contents.json | Watch widget extension icon filename update. |
| WatchWidgets/Assets.xcassets/AccentColor.colorset/Contents.json | Accent color asset metadata. |
| Sources/WatchApp/WatchSettingsViewModel.swift | Adds “delete local data” capability (DB wipe + cache cleanup). |
| Sources/WatchApp/WatchSettingsView.swift | Adds UI for delete-local-data + client events view entry point. |
| Sources/WatchApp/WatchServerSync.swift | Shared server + certificate sync routine and URL override re-application. |
| Sources/WatchApp/WatchServerDetailView.swift | New server detail UI (URL override picker + mTLS identity status). |
| Sources/WatchApp/WatchNotificationNames.swift | Adds watch-local Notification.Name helpers for sync/refresh triggers. |
| Sources/WatchApp/WatchConfigAssistViewModel.swift | Removes magic-item JSON caching and relies on GRDB-backed info. |
| Sources/WatchApp/WatchConfigAssistView.swift | Adds on-watch Assist configuration screen. |
| Sources/WatchApp/WatchCommunicatorService.swift | New immediate communicator observer service for Assist events. |
| Sources/WatchApp/Utilities/WKInterfaceLabel+Additions.swift | Utility for hiding labels when empty. |
| Sources/WatchApp/Utilities/WKInterfaceDevice+Size.swift | Adds watch size detection helper. |
| Sources/WatchApp/Notifications/NotificationSubControllerMJPEG.swift | Adds MJPEG camera streaming notification renderer. |
| Sources/WatchApp/Notifications/NotificationSubControllerMedia.swift | Adds image/video attachment notification renderer. |
| Sources/WatchApp/Notifications/NotificationSubControllerMap.swift | Adds map-based notification renderer + region helper. |
| Sources/WatchApp/Notifications/NotificationSubController.swift | Defines common protocol + element container for notification renderers. |
| Sources/WatchApp/Notifications/DynamicNotificationController.swift | New dynamic notification controller + loading indicator. |
| Sources/WatchApp/Info.plist | Removes ClockKit complication keys; adds URL schemes for deep links. |
| Sources/WatchApp/HostingController.swift | Switches to SwiftUI App lifecycle; retains legacy HostingController stub. |
| Sources/WatchApp/Home/WatchHomeViewModel+Editing.swift | Adds on-watch editing/mutation/persistence utilities for watch config. |
| Sources/WatchApp/Home/WatchHomeView.swift | Adds Assist widget URL handling + sync error banner; improves cold-launch behavior. |
| Sources/WatchApp/Home/WatchHomeHeaderView.swift | Improves loading UI with progress/status text. |
| Sources/WatchApp/Home/WatchHomeFooterView.swift | Adds footer row with version/SSID + edit/settings buttons. |
| Sources/WatchApp/Home/WatchFolderContentView.swift | Adds folder drill-in UI with edit/reorder controls. |
| Sources/WatchApp/Home/MagicItemRow/WatchMagicViewRowViewModel.swift | New view model for executing MagicItems with feedback + error surfacing. |
| Sources/WatchApp/Home/MagicItemRow/WatchMagicViewRow.swift | New SwiftUI row rendering + haptics + error alerts for MagicItems. |
| Sources/WatchApp/Home/MagicItemRow/WatchHomeItemLabel.swift | Shared row label layout + icon container styling helpers. |
| Sources/WatchApp/Home/MagicItemRow/WatchFolderRow.swift | Folder row rendering matching MagicItem row styling. |
| Sources/WatchApp/Home/Edit/WatchIconPicker.swift | On-watch MaterialDesignIcons picker (search + grid). |
| Sources/WatchApp/Home/Edit/WatchConfigItemRow.swift | Shared config row content + reorder controls + row styling helpers. |
| Sources/WatchApp/Home/Edit/WatchConfigItemEditView.swift | On-watch name/icon/color/confirmation editor with live preview. |
| Sources/WatchApp/Home/Edit/WatchConfigAddView.swift | On-watch add flow (entity/folder) with search and domain filtering. |
| Sources/WatchApp/Home/Edit/WatchColorPicker.swift | On-watch color palette picker replacement for ColorPicker. |
| Sources/WatchApp/Home/CircularGlassOrLegacyBackground.swift | Centralizes watchOS 26 glass-effect vs legacy styling for rows/buttons. |
| Sources/WatchApp/ExtensionDelegate.swift | Removes ClockKit launch handling; adds widget snapshot export + Assist deep-link handling. |
| Sources/WatchApp/Complication/Assist/AssistDefaultComplication.swift | New Assist default complication constants (now used by widget/deep links). |
| Sources/WatchApp/Assist/WatchAudioRecorder.swift | Adds watch audio recording with silence detection. |
| Sources/WatchApp/Assist/WatchAssistViewModel.swift | Assist flow orchestration on watch (recording, ping/pong, error handling). |
| Sources/WatchApp/Assist/WatchAssistView+Build.swift | Factory for building WatchAssistView with concrete dependencies. |
| Sources/WatchApp/Assist/WatchAssistView.swift | Assist UI on watch (chat bubbles, mic button, loading states, volume). |
| Sources/WatchApp/Assist/WatchAssistService.swift | Sends Assist audio to iPhone via chunked immediate messages. |
| Sources/WatchApp/Assist/Views/VolumeView.swift | Watch volume control wrapper (WKInterfaceVolumeControl). |
| Sources/WatchApp/Assist/Views/LabsLabel.swift | “Labs” badge UI for Assist screen. |
| Sources/WatchApp/Assist/Views/ChatBubbleView.swift | Chat bubble styling + alignment per Assist item type. |
| Sources/WatchApp/Assets.xcassets/TemplateLogo.imageset/Contents.json | Removes auto-scaling property from watch asset metadata. |
| Sources/WatchApp/Assets.xcassets/RoundLogo.imageset/Contents.json | Removes auto-scaling property from watch asset metadata. |
| Sources/WatchApp/Assets.xcassets/Contents.json | Normalizes asset catalog JSON formatting. |
| Sources/Watch/WatchCommunicatorService.swift | Adds guaranteed-message fallback for config pulls; adds chunked DB mirror streaming. |
| Sources/Shared/Watch/WatchDatabaseMirror.swift | Updates documentation for chunked/streamed mirror delivery. |
| Sources/Shared/Watch/InteractiveImmediateMessages.swift | Adds mirror chunk message/response types; updates docs for new protocol. |
| Sources/Shared/Resources/Swiftgen/Strings.swift | SwiftGen output update for new watch strings. |
| Sources/Shared/Database/GRDB+Initialization.swift | Adds eraseAllData() helper used by the watch “Delete local data” action. |
| Sources/Extensions/Watch/Utilities/Date+ComplicationDivination.swift | Removes legacy ClockKit date encoding utility. |
| Sources/Extensions/Watch/Resources/Info.plist | Removes legacy watch extension Info.plist (ClockKit path). |
| Sources/Extensions/Watch/Resources/Assets.xcassets/Contents.json | Removes legacy watch extension assets catalog root. |
| Sources/Extensions/Watch/Resources/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json | Removes legacy complication image set metadata. |
| Sources/Extensions/Watch/Resources/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json | Removes legacy complication image set metadata. |
| Sources/Extensions/Watch/Resources/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json | Removes legacy complication image set metadata. |
| Sources/Extensions/Watch/Resources/Assets.xcassets/Complication.complicationset/Graphic Extra Large.imageset/Contents.json | Removes legacy complication image set metadata. |
| Sources/Extensions/Watch/Resources/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json | Removes legacy complication image set metadata. |
| Sources/Extensions/Watch/Resources/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json | Removes legacy complication image set metadata. |
| Sources/Extensions/Watch/Resources/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json | Removes legacy complication image set metadata. |
| Sources/Extensions/Watch/Resources/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json | Removes legacy complication image set metadata. |
| Sources/Extensions/Watch/Resources/Assets.xcassets/Complication.complicationset/Contents.json | Removes legacy complicationset descriptor. |
| Sources/Extensions/Watch/Resources/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json | Removes legacy complication image set metadata. |
| Sources/Extensions/Watch/HostingController.swift | Removes legacy watch extension HostingController implementation. |
| Sources/Extensions/Watch/Home/WatchHomeViewModel.swift | Removes legacy watch extension home view model (superseded by new watch app flow). |
| Sources/Extensions/Watch/Complication/ComplicationController.swift | Removes legacy ClockKit complication data source. |
| Sources/Extensions/Watch/Complication/Assist/AssistDefaultComplication.swift | Removes legacy ClockKit Assist complication implementation. |
| Sources/App/Resources/en.lproj/Localizable.strings | Adds English strings for new watch sync/troubleshooting/toast UI. |
| HomeAssistant.xcodeproj/xcshareddata/xcschemes/WatchWidgetsExtension.xcscheme | Adds a shared Xcode scheme for the new watch widgets extension. |
| HomeAssistant.xcodeproj/xcshareddata/xcschemes/WatchApp.xcscheme | Updates scheme launcher/debugger settings. |
| HomeAssistant.xcodeproj/xcshareddata/xcschemes/WatchApp (Complication).xcscheme | Updates scheme launcher/debugger settings. |
| fastlane/README.md | Documents install_git_hooks lane usage. |
|
@copilot resolve the merge conflicts on this branch. |
Resolved by merging |
|
@copilot fix lint issues reported by CI |
Fixed in |
|
@copilot resolve the merge conflicts on this branch. |
|
Found 3 unused localization strings in the codebase. Click to see detailsTo remove them, run the |
Resolved in |
The WatchWidgets extension added in #5027 broke the App-Release archive in the Distribute workflow with: Extensions-WatchWidgets has conflicting provisioning settings. Extensions-WatchWidgets is automatically signed, but provisioning profile iOS App Store - Extensions-WatchWidgets has been manually specified. Two things were wrong: 1. The target was created via the Xcode new-target wizard, which hard-coded CODE_SIGN_STYLE = Automatic in its Debug and Release build configs. Every other shipping target leaves this unset and inherits it from the xcconfigs (Automatic for Debug, Manual for the App-Release archive). The target-level Automatic overrode the xcconfig's Manual while the release xcconfig's PROVISIONING_PROFILE_SPECIFIER[sdk=watchos*] = "iOS App Store - $(TARGET_NAME)" was still inherited, producing the conflict. Removing the two lines lets WatchWidgets sign exactly like WatchApp and the other extensions. 2. No App Store provisioning profile existed for the new bundle id io.robbie.HomeAssistant.watchkitapp.WatchWidgets. CI imports the profiles committed under Configuration/Provisioning, so the newly created "iOS App Store - Extensions-WatchWidgets" profile is added there. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The WatchWidgets extension added in #5027 has no App Store provisioning profile for its bundle id io.robbie.HomeAssistant.watchkitapp.WatchWidgets, so the App-Release archive in the Distribute workflow fails to sign it. CI imports the profiles committed under Configuration/Provisioning (setup_ha_ci -> import_provisioning_profiles), so the newly created "iOS App Store - Extensions-WatchWidgets" profile is added there alongside the rest. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…5033) ## Summary Follow-up to #5032. Committing the provisioning profile was necessary but not sufficient: the **Distribute** workflow's `App-Release` archive still fails with the same error ([latest failing run](https://github.com/home-assistant/iOS/actions/runs/29084034712/job/86333386325)): > `Extensions-WatchWidgets has conflicting provisioning settings. Extensions-WatchWidgets is automatically signed, but provisioning profile iOS App Store - Extensions-WatchWidgets has been manually specified.` `Extensions-WatchWidgets` (added in #5027) was created via the Xcode new-target wizard, which hard-coded `CODE_SIGN_STYLE = Automatic` into its Debug **and** Release build configs. Every other shipping target (`App`, `WatchApp`, all `Extensions-*`) leaves `CODE_SIGN_STYLE` unset at the target level and inherits it from the xcconfigs: - `Configuration/HomeAssistant.xcconfig` → `Automatic` for Debug - `Configuration/HomeAssistant.release.xcconfig` → `Manual` for the archive, plus `PROVISIONING_PROFILE_SPECIFIER[sdk=watchos*] = iOS App Store - $(TARGET_NAME)` The target-level `Automatic` overrode the xcconfig's `Manual` while the manual profile specifier was still inherited. xcodebuild rejects that combination at signing-settings resolution — **before** the profile is even used — which is why adding the profile in #5032 didn't clear it. Removing the two lines lets WatchWidgets resolve signing exactly like `WatchApp`: `Manual` + the `iOS App Store - Extensions-WatchWidgets` profile for the release archive, `Automatic` for local Debug builds. ## Changes - Remove target-level `CODE_SIGN_STYLE = Automatic` from `Extensions-WatchWidgets` (Debug + Release). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Screenshots
Link to pull request in Documentation repository
Documentation: home-assistant/companion.home-assistant#
Any other notes