Watch folders PR with fixed pbxproj#4396
Conversation
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…nto watch-folders
…ew.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…rationView.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…rationView.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds “folder” support to the Apple Watch Magic Items configuration, allowing users to group items into folders in the iOS configuration UI and navigate into those folders in the watchOS app.
Changes:
- Extend
MagicItem/MagicItemProvider/watch communication to support a new.folderitem type and include folder children when sending watch config metadata. - Add iOS configuration UI for creating folders and managing folder contents (add/reorder/delete items, edit folder appearance).
- Add watchOS UI to render folders as rows and show folder contents in a dedicated view, plus update tests.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| Tests/App/Watch/WatchConfig.test.swift | Adds unit tests covering folder operations in WatchConfigurationViewModel. |
| Sources/Watch/WatchCommunicatorService.swift | Includes folder children when collecting magicItemsInfo; prevents executing folder “items”. |
| Sources/Shared/Resources/Swiftgen/Strings.swift | Adds generated SwiftGen accessors for new watch folder strings. |
| Sources/Shared/MagicItem/MagicItemProvider.swift | Adds getInfo(for:) support for .folder. |
| Sources/Shared/MagicItem/MagicItem.swift | Adds items (children) and new .folder item type. |
| Sources/Extensions/Watch/Home/WatchHomeViewModel.swift | Replaces list refresh workaround with a config version UUID. |
| Sources/Extensions/Watch/Home/WatchHomeView.swift | Adds folder navigation behavior and uses configVersion to force list refresh. |
| Sources/Extensions/Watch/Home/WatchFolderContentView.swift | New watchOS view to display folder contents. |
| Sources/Extensions/Watch/Home/MagicItemRow/WatchMagicViewRow.swift | Refactors icon color usage to avoid repeated hex parsing. |
| Sources/Extensions/Watch/Home/MagicItemRow/WatchFolderRow.swift | New watchOS row UI for folders. |
| Sources/App/Settings/AppleWatch/HomeCustomization/WatchConfigurationViewModel.swift | Adds folder CRUD/move logic in config view model. |
| Sources/App/Settings/AppleWatch/HomeCustomization/WatchConfigurationView.swift | Adds “Add Folder” sheet and folder detail navigation; updates icon coloring logic. |
| Sources/App/Settings/AppleWatch/HomeCustomization/FolderEditView.swift | New iOS view to edit folder name/icon/colors. |
| Sources/App/Settings/AppleWatch/HomeCustomization/FolderDetailView.swift | New iOS view to manage items inside a folder. |
| Sources/App/Resources/en.lproj/Localizable.strings | Adds new English localization keys for folder UI. |
| HomeAssistant.xcodeproj/project.pbxproj | Registers new Swift source files in the Xcode project/targets. |
| func moveItemToFolder(itemId: String, serverId: String, toFolderId: String) { | ||
| // Remove from root if present | ||
| if let rootIndex = watchConfig.items.firstIndex(where: { $0.id == itemId && $0.serverId == serverId }) { | ||
| let item = watchConfig.items.remove(at: rootIndex) | ||
| addItemToFolder(folderId: toFolderId, item: item) | ||
| return | ||
| } | ||
| // Remove from any folder if present | ||
| for (folderIndex, folder) in watchConfig.items.enumerated() where folder.type == .folder { | ||
| if var items = folder.items, | ||
| let index = items.firstIndex(where: { $0.id == itemId && $0.serverId == serverId }) { | ||
| let item = items.remove(at: index) | ||
| var updatedFolder = folder | ||
| updatedFolder.items = items | ||
| watchConfig.items[folderIndex] = updatedFolder | ||
| addItemToFolder(folderId: toFolderId, item: item) | ||
| return | ||
| } |
There was a problem hiding this comment.
moveItemToFolder removes the item from the root / source folder before verifying that toFolderId exists. If the destination folder ID is invalid (or the folder was deleted concurrently), addItemToFolder will no-op and the item is effectively lost. Consider first validating the destination folder index (or making addItemToFolder return a Bool) and only removing the item when the destination write succeeds; otherwise, keep the item in its original location.
| case .folder: | ||
| return .init( | ||
| id: item.serverUniqueId, | ||
| name: item.displayText ?? L10n.Watch.Configuration.Folder.defaultName, | ||
| iconName: MaterialDesignIcons.folderIcon.name, | ||
| customization: item.customization | ||
| ) |
There was a problem hiding this comment.
Folders introduce nested MagicItems, but the watch-config migration (migrateItemsIfNeeded) only considers the root items array. This means any server-ID repair/migration logic will be skipped for items inside folder.items, and those children can remain broken after server re-add/ID changes. Consider updating the migration logic to recursively migrate children of .folder items (and then persist the updated config).
| .onChange(of: useCustomColors) { newValue in | ||
| if newValue { | ||
| folder.customization?.backgroundColor = folder.customization?.backgroundColor ?? UIColor.black | ||
| .hexString() | ||
| folder.customization?.textColor = folder.customization?.textColor ?? UIColor.white.hexString() | ||
| } else { | ||
| folder.customization?.backgroundColor = nil | ||
| folder.customization?.textColor = nil | ||
| } |
There was a problem hiding this comment.
folder.customization is optional, but several setters here use optional chaining (e.g., folder.customization?.backgroundColor = ...). If customization is nil (which can happen when decoding older configs), toggling custom colors and the color pickers will silently do nothing. Initializing folder.customization in init (or inside the onChange before writing) would make the edit screen robust without relying on onAppear timing.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4396 +/- ##
=======================================
Coverage ? 42.75%
=======================================
Files ? 265
Lines ? 15505
Branches ? 0
=======================================
Hits ? 6629
Misses ? 8876
Partials ? 0 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@joelhawksley It's in TestFlight already, thank you for your contribution! Quite cool feature 🚀 |
#4376, but with pbxproj rebuilt with minimum changes.