Skip to content

Migrate Notification category/action editors to SwiftUI#4563

Merged
bgoncal merged 5 commits into
mainfrom
claude/eureka-notification-categories-swiftui
Apr 29, 2026
Merged

Migrate Notification category/action editors to SwiftUI#4563
bgoncal merged 5 commits into
mainfrom
claude/eureka-notification-categories-swiftui

Conversation

@bgoncal
Copy link
Copy Markdown
Member

@bgoncal bgoncal commented Apr 24, 2026

Summary

Migrate the notification category list and the category/action editors to SwiftUI:

  • NotificationCategoryListView: local insert/delete, Realm-observed server categories section, navigation to editor.
  • NotificationCategoryEditorView: required fields, server-controlled read-only mode, hidden preview/summary text areas, reorder/insert/delete action rows, YAML service-call preview, toolbar help, preview-notification action.
  • NotificationActionEditorView: required title/identifier, conditional text-input fields, foreground/destructive/authentication toggles, server-controlled read-only mode, YAML trigger preview.

Also adds three reusable components:

  • YamlPreviewSection — replaces Eureka YamlSection within this slice; generalises the inline version previously in NFCTagView.
  • NotificationIdentifierField — SwiftUI text-field helper enforcing identifier casing/validation (replaces NotificationIdentifierEurekaRow).
  • RealmResultsObserverObservableObject wrapping AnyRealmCollection with a notification token (reusable by future slices).

Deletes the three old *Configurator.swift / *ViewController.swift files plus NotificationIdentifierEurekaRow.swift.

Screenshots

Pending — to be added before merge.

Link to pull request in Documentation repository

Documentation: home-assistant/companion.home-assistant#

Any other notes

Part of a five-PR Eureka → SwiftUI migration tracked in UIKitToSwiftUIMigration.md (siblings: #4560, #4561, #4562, #4564). The Eureka, ColorPickerRow, and ViewRow pods stay until all slices land.

Reconciliation with #4562: this PR embeds NotificationCategoryListView inside the old NotificationSettingsViewController via UIHostingController so the list is reachable while the parent screen is still Eureka. Once #4562 merges, NotificationSettingsView should link directly to NotificationCategoryListView — the hosting-controller wrapper becomes unnecessary.

YamlSection.swift and RealmSection.swift are retained — still used by ActionConfigurator, NFCTagViewController, SettingsDetailViewController, and ComplicationListViewController.

bundle exec fastlane lint passes. Not build-verified locally yet.

🤖 Generated with Claude Code

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates the notification category/action configuration UI from Eureka/UIKit to SwiftUI, adding reusable SwiftUI components for Realm observation, identifier input, and YAML previews, while temporarily embedding the new list view from the existing NotificationSettingsViewController.

Changes:

  • Replace legacy Eureka-based notification category/action editors with SwiftUI NotificationCategoryListView, NotificationCategoryEditorView, and NotificationActionEditorView.
  • Add reusable SwiftUI components: YamlPreviewSection, NotificationIdentifierField/NotificationIdentifierTextField, and RealmResultsObserver.
  • Remove the old *Configurator.swift/*ViewController.swift files and the Eureka identifier row; update the Xcode project to include new sources.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
Sources/App/Utilities/NotificationIdentifierEurekaRow.swift Deletes legacy Eureka identifier row implementation.
Sources/App/Settings/Notifications/NotificationSettingsViewController.swift Embeds new SwiftUI category list via UIHostingController.
Sources/App/Settings/Notifications/NotificationCategoryListViewController.swift Removes legacy Eureka category list controller.
Sources/App/Settings/Notifications/NotificationCategoryListView.swift Adds SwiftUI category list with local/server sections and sheet-based navigation to editor.
Sources/App/Settings/Notifications/NotificationCategoryEditorView.swift Adds SwiftUI category editor with validation, action management, YAML preview, help, and preview notification.
Sources/App/Settings/Notifications/NotificationCategoryConfigurator.swift Removes legacy Eureka category configurator.
Sources/App/Settings/Notifications/NotificationActionEditorView.swift Adds SwiftUI action editor with validation, conditional text input, toggles, and YAML preview.
Sources/App/Settings/Notifications/NotificationActionConfigurator.swift Removes legacy Eureka action configurator.
Sources/App/Settings/Components/YamlPreviewSection.swift Adds reusable SwiftUI YAML preview + share + full-screen view.
Sources/App/Settings/Components/RealmResultsObserver.swift Adds ObservableObject wrapper for Realm collection updates.
Sources/App/Settings/Components/NotificationIdentifierField.swift Adds sanitization/validation + SwiftUI text field wrapper for identifiers.
HomeAssistant.xcodeproj/project.pbxproj Wires in new SwiftUI files and removes deleted legacy files.

Comment thread Sources/App/Settings/Components/YamlPreviewSection.swift
Comment thread Sources/App/Settings/Components/NotificationIdentifierField.swift Outdated
Comment thread Sources/App/Settings/Notifications/NotificationSettingsViewController.swift Outdated
Comment thread Sources/App/Settings/Notifications/NotificationCategoryEditorView.swift Outdated
Comment thread Sources/App/Settings/Components/RealmResultsObserver.swift
@bgoncal bgoncal force-pushed the claude/eureka-notification-categories-swiftui branch from 898f5c9 to b829432 Compare April 28, 2026 12:01
bgoncal added a commit that referenced this pull request Apr 28, 2026
## Summary
Replace the Eureka-based location settings screen with a SwiftUI
`LocationSettingsView` + `LocationSettingsViewModel`. Includes
permission status rows, background refresh, location history link,
manual location update, update-source toggles, and per-zone read-only
sections. Zone detail now uses an `MKMapView`-backed SwiftUI preview
with the zone radius overlay. `PermissionStatusRow.swift` and
`EurekaLocationRow.swift` are removed; permission/disabled-state logic
moves into the view model. `SettingsItem.location` is routed directly to
the SwiftUI view and the location setup (~270 lines) is trimmed from
`SettingsDetailViewController`.

## Screenshots

## Link to pull request in Documentation repository
Documentation: home-assistant/companion.home-assistant#

## Any other notes
Part of a five-PR Eureka → SwiftUI migration tracked in
`UIKitToSwiftUIMigration.md`. The `Eureka`, `ColorPickerRow`, and
`ViewRow` pods stay until the remaining four slices land: actions
(#4561), notification settings (#4562), notification categories (#4563),
watch complications (#4564).

Build-verified locally on iOS simulator.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
@bgoncal bgoncal force-pushed the claude/eureka-notification-categories-swiftui branch from 6a0dd4c to 46098d7 Compare April 28, 2026 14:20
bgoncal added a commit that referenced this pull request Apr 28, 2026
## Summary
Replace the Eureka actions list with `ActionsSettingsView` +
`ActionsSettingsViewModel` (value-type Realm snapshots, delete/reorder,
scene toggle + customize flow, server refresh). Replace
`ActionConfigurator` with `ActionConfiguratorView` using SwiftUI `Form`,
`IconPicker`, SwiftUI `ColorPicker`, the existing
`WidgetBasicButtonView` preview, and a new inline `ActionServerPicker`.
`SettingsItem.actions` is wired directly to the SwiftUI view and the
Eureka actions code is removed from `SettingsDetailViewController`.

## Screenshots
_Pending — to be added before merge._

## Link to pull request in Documentation repository
Documentation: home-assistant/companion.home-assistant#

## Any other notes
Part of a five-PR Eureka → SwiftUI migration tracked in
`UIKitToSwiftUIMigration.md` (siblings: #4560, #4562, #4563, #4564). The
`Eureka`, `ColorPickerRow`, and `ViewRow` pods stay until all slices
land.

`ServerSelectRow.swift` is intentionally kept in-tree because
`ComplicationEditViewController` still uses it — both come out once the
complications slice (#4564) merges.

Not build-verified locally yet; may surface access-level fixes (e.g.
internal synthesized initializers on Shared types) during build.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
bgoncal added a commit that referenced this pull request Apr 28, 2026
## Summary
Migrate the notification settings screen and its three leaf screens to
SwiftUI:
- `NotificationSettingsView`: permission status, learn-more link, sounds
/ categories / rate-limit / debug navigation, badge reset + auto-clear,
push ID share, push ID reset.
- `NotificationSoundsView`: imported / bundled / system segmented lists,
audio playback, swipe delete, `.fileImporter`, file-sharing + system
import, AKConverter progress HUD, alert handling.
- `NotificationRateLimitView`: pull-to-refresh on iOS, toolbar refresh
on Catalyst, 1-second reset countdown, retry state, parent
remaining-count callback.
- `NotificationDebugNotificationsView`: `UserDefaults`-backed toggles.

Removes the Eureka `row(for:)` extensions in
`NotificationRateLimitsAPI.swift`, deletes the four old
`*ViewController.swift` files, and rewires `SettingsItem.notifications`
and `NotificationManager.openSettingsFor` to the SwiftUI view.

## Screenshots
_Pending — to be added before merge._

## Link to pull request in Documentation repository
Documentation: home-assistant/companion.home-assistant#

## Any other notes
Part of a five-PR Eureka → SwiftUI migration tracked in
`UIKitToSwiftUIMigration.md` (siblings: #4560, #4561, #4563, #4564). The
`Eureka`, `ColorPickerRow`, and `ViewRow` pods stay until all slices
land.

**Reconciliation with #4563:** the categories PR temporarily embeds
`NotificationCategoryListView` inside the old
`NotificationSettingsViewController` via `UIHostingController`. This PR
deletes that controller entirely; after both merge, the
`categoriesDestination` in `NotificationSettingsView` should link
directly to `NotificationCategoryListView` from the categories PR.

`bundle exec fastlane lint` passes. Not build-verified locally yet.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
bgoncal and others added 3 commits April 29, 2026 01:00
Replaces the Eureka-based NotificationCategoryListViewController,
NotificationCategoryConfigurator, and NotificationActionConfigurator
with SwiftUI equivalents (NotificationCategoryListView,
NotificationCategoryEditorView, NotificationActionEditorView).

Adds three new reusable helpers under Sources/App/Settings/Components:
- YamlPreviewSection: replaces Eureka YamlSection for this slice,
  generalising the preview/share pattern already used in NFCTagView.
- NotificationIdentifierField: SwiftUI text-field/formatter replacement
  for the old NotificationIdentifierEurekaRow, enforcing identifier
  casing and validation.
- RealmResultsObserver: ObservableObject wrapper around
  AnyRealmCollection using a Realm notification token, avoiding direct
  Realm access in view bodies.

Wires the categories list into NotificationSettingsViewController via a
UIHostingController and removes the migrated Eureka helpers exclusive to
this slice. YamlSection and RealmSection are retained since they are
still used by other (out-of-scope) controllers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Update YamlPreviewSection doc to reflect that yaml is a String value
  rather than a closure.
- Update sanitize doc to match implementation: spaces become underscores
  and other invalid characters are dropped.
- Pass the hosting view controller as presenter to openURLInBrowser from
  NotificationCategoryListView and NotificationCategoryEditorView so the
  SafariInApp browser preference works (was crashing on nil presenter).
- Embed NotificationCategoryListView via embeddedInHostingController so it
  has a ViewControllerProvider in scope, and forward the provider to the
  editor sheet.
- Uppercase the categoryIdentifier in the preview notification so it
  matches the registered UNNotificationCategory.
- Make RealmResultsObserver MainActor-isolated, snapshot synchronously and
  hop to MainActor to publish, matching the doc comment.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`if` directly inside a `@ToolbarContentBuilder` body requires the
iOS 16+ ToolbarContentBuilder. Always emit each `ToolbarItem` and
gate its content on `!isServerControlled` instead — that puts the
conditional in a regular ViewBuilder context which compiles on iOS 15.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@bgoncal bgoncal force-pushed the claude/eureka-notification-categories-swiftui branch from 46098d7 to fb91def Compare April 28, 2026 23:01
The rebase carried forward duplicate group entries and PBXBuildFile /
PBXFileReference rows for the legacy `*ViewController.swift` and
`*Configurator.swift` files. Two of those reused the same UUIDs as
their renamed `*View.swift` replacements, so Xcode's parser resolved
the UUID to the deleted `*ViewController.swift` path and the build
failed with "Build input files cannot be found" for
`NotificationRateLimitViewController.swift` and
`NotificationDebugNotificationsViewController.swift`.

Drops every reference to:
- NotificationCategoryConfigurator.swift
- NotificationActionConfigurator.swift
- NotificationCategoryListViewController.swift
- NotificationSoundsViewController.swift (duplicate UUID)
- NotificationSettingsViewController.swift (duplicate UUID)
- NotificationRateLimitViewController.swift (duplicate UUID)
- NotificationDebugNotificationsViewController.swift (duplicate UUID)
- the duplicated NotificationRateLimitsAPI.swift group entry

Co-Authored-By: Claude <noreply@anthropic.com>
The category-list slice still embedded the legacy
`NotificationCategoryListViewController` via the temporary
`embed(...)` UIKit bridge. With that controller deleted in this PR,
build was failing with "cannot find 'NotificationCategoryListViewController'
in scope". Swap the destination to `NotificationCategoryListView()`,
which already sets its own navigation title.

Co-Authored-By: Claude <noreply@anthropic.com>
bgoncal added a commit that referenced this pull request Apr 28, 2026
The Eureka-based settings tree was fully replaced by SwiftUI across
PRs #4560#4564. Every remaining Eureka file on main is dead code:
`SettingsRootDataSource.swift` has no inbound references, the active
settings root (`SettingsItem.swift`) routes directly to the SwiftUI
views (`NFCListView`, `ComplicationListView`, etc.), and the legacy
`*ViewController.swift` / Eureka helper-row files are reachable only
from each other. Tests don't import Eureka, and the `ColorPickerRow`
and `ViewRow` add-on pods aren't referenced anywhere either.

Removes:
- `Sources/App/Settings/Eureka/` (14 helper rows + form VC + utils)
- `Sources/App/Settings/MaterialDesignIcons+Eureka.swift`
  (`SearchItem` conformance for the now-deleted `SearchPushRow`)
- `Sources/App/Settings/SettingsRootDataSource.swift`
- `Sources/App/Settings/Settings/SettingsDetailViewController.swift`
- `Sources/App/Settings/AppleWatch/Complication{List,FamilySelect,Edit}ViewController.swift`
- `Sources/App/Settings/NFC/NFC{List,Tag}ViewController.swift`
- `Sources/App/Utilities/{SearchPushRow,WebSocketStatusRow}.swift`
- `Eureka`, `ColorPickerRow`, `ViewRow` pods from `Podfile`/`Podfile.lock`
- `import Eureka` and `-framework "Eureka"` LDFLAGS from project files

No behavior change — pure dead-code removal. Stacked on #4563.

Co-Authored-By: Claude <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 29, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@aa962d0). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4563   +/-   ##
=======================================
  Coverage        ?   43.24%           
=======================================
  Files           ?      275           
  Lines           ?    16700           
  Branches        ?        0           
=======================================
  Hits            ?     7222           
  Misses          ?     9478           
  Partials        ?        0           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bgoncal bgoncal merged commit 814152e into main Apr 29, 2026
15 checks passed
@bgoncal bgoncal deleted the claude/eureka-notification-categories-swiftui branch April 29, 2026 00:16
bgoncal added a commit that referenced this pull request Apr 29, 2026
The Eureka-based settings tree was fully replaced by SwiftUI across
PRs #4560#4564. Every remaining Eureka file on main is dead code:
`SettingsRootDataSource.swift` has no inbound references, the active
settings root (`SettingsItem.swift`) routes directly to the SwiftUI
views (`NFCListView`, `ComplicationListView`, etc.), and the legacy
`*ViewController.swift` / Eureka helper-row files are reachable only
from each other. Tests don't import Eureka, and the `ColorPickerRow`
and `ViewRow` add-on pods aren't referenced anywhere either.

Removes:
- `Sources/App/Settings/Eureka/` (14 helper rows + form VC + utils)
- `Sources/App/Settings/MaterialDesignIcons+Eureka.swift`
  (`SearchItem` conformance for the now-deleted `SearchPushRow`)
- `Sources/App/Settings/SettingsRootDataSource.swift`
- `Sources/App/Settings/Settings/SettingsDetailViewController.swift`
- `Sources/App/Settings/AppleWatch/Complication{List,FamilySelect,Edit}ViewController.swift`
- `Sources/App/Settings/NFC/NFC{List,Tag}ViewController.swift`
- `Sources/App/Utilities/{SearchPushRow,WebSocketStatusRow}.swift`
- `Eureka`, `ColorPickerRow`, `ViewRow` pods from `Podfile`/`Podfile.lock`
- `import Eureka` and `-framework "Eureka"` LDFLAGS from project files

No behavior change — pure dead-code removal. Stacked on #4563.

Co-Authored-By: Claude <noreply@anthropic.com>
bgoncal added a commit that referenced this pull request Apr 29, 2026
## Summary

Now that PRs #4560#4564 have migrated every Eureka-based settings
screen to SwiftUI, every remaining Eureka file on `main` is dead code.
This PR removes them, the Eureka pod, and the two Eureka-community
add-on pods.

**Reachability check before deleting:**

- `SettingsRootDataSource.swift` (the legacy Eureka settings root) has
zero inbound references. The active settings root is
`SettingsItem.swift`, which already routes to SwiftUI views
(`NFCListView()`, `ComplicationListView()`, etc.).
- The legacy `*ViewController.swift` files (`NFCListViewController`,
`ComplicationListViewController`, `SettingsDetailViewController`, etc.)
are reachable only from `SettingsRootDataSource` and from each other.
- The Eureka helper-row library (`Sources/App/Settings/Eureka/`) is used
only by those legacy view controllers.
- A handful of doc-comment mentions in active SwiftUI code (e.g. `//
SwiftUI replacement for ButtonRowWithLoading`) are not real references.
- Tests don't import Eureka. `ColorPickerRow` and `ViewRow` (Eureka
community pods) aren't referenced anywhere.

## What's removed

- `Sources/App/Settings/Eureka/` — 14 files (`HAFormViewController`,
helper rows, `AccountInitialsImage`)
- `Sources/App/Settings/MaterialDesignIcons+Eureka.swift` — `SearchItem`
conformance for the now-deleted `SearchPushRow`
- `Sources/App/Settings/SettingsRootDataSource.swift`
- `Sources/App/Settings/Settings/SettingsDetailViewController.swift`
-
`Sources/App/Settings/AppleWatch/Complication{List,FamilySelect,Edit}ViewController.swift`
- `Sources/App/Settings/NFC/NFC{List,Tag}ViewController.swift`
- `Sources/App/Utilities/{SearchPushRow,WebSocketStatusRow}.swift`
- `Eureka`, `ColorPickerRow`, `ViewRow` pods (Podfile + Podfile.lock)
- `import Eureka` from `SettingsSceneDelegate.swift`
- `-framework "Eureka"` from inlined `OTHER_LDFLAGS` in
`project.pbxproj`

24 files deleted, 4 modified. Pure dead-code removal — no behavior
changes.

## Stacked on #4563

This PR targets `claude/eureka-notification-categories-swiftui` (PR
#4563). After #4563 merges, GitHub will auto-rebase the base to `main`.

## Test plan

- [ ] CI lint + test passes
- [ ] Build succeeds with the smaller pod set
- [ ] Manual smoke test: open Settings → NFC, Settings → Complications,
Notifications → Categories — all SwiftUI screens still functional

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants