Skip to content

feat: replace Preferences window with SwiftUI#231

Merged
2ndalpha merged 3 commits intomasterfrom
swiftui-preferences
Feb 28, 2026
Merged

feat: replace Preferences window with SwiftUI#231
2ndalpha merged 3 commits intomasterfrom
swiftui-preferences

Conversation

@2ndalpha
Copy link
Owner

@2ndalpha 2ndalpha commented Feb 28, 2026

Summary

  • Migrates the Preferences window from ObjC PreferenceController + Preferences.xib to SwiftUI, following the pattern from feat: replace URLSheet with SwiftUI #227 (URLSheet)
  • Uses NSTabViewController with tabStyle = .toolbar and toolbarStyle = .preference for proper macOS preference toolbar with SF Symbol icons
  • Fixed content size across all tabs prevents window resizing on tab switch
  • Each tab is a SwiftUI view wrapped in NSHostingController: General, Editor, Remote, Hotkeys, Update
  • Wraps SRRecorderControl (ShortcutRecorder) and SUUpdater (Sparkle) for SwiftUI via NSViewRepresentable and ObservableObject
  • Deletes Preferences.xib, PreferenceController.h/.m, UpdateDateTransformer.h/.m, and unused preference tab icon PNGs (−685 lines)

Screenshot

Preferences Window

New files

File Role
PreferencesView.swift SwiftUI views for each of the 5 tabs
PreferencesPresenter.swift @objc bridge with NSTabViewController setup
RemoteIntervalMapper.swift Pure enum mapping slider positions ↔ intervals
ShortcutRecorderView.swift NSViewRepresentable wrapping RecorderControl
SparkleObserver.swift ObservableObject wrapping SUUpdater.shared()
LoginItemObserver.swift ObservableObject wrapping ObjC LoginItem

Tab icons (SF Symbols)

Tab Symbol
General gearshape
Editor square.and.pencil
Remote globe
Hotkeys command.square.fill
Update arrow.triangle.2.circlepath

Test plan

  • All 78 tests pass (70 existing + 8 new)
  • Open Preferences from the menu bar — window appears with toolbar-style tabs and SF Symbol icons
  • Window size stays consistent when switching between tabs
  • General tab: toggle Open at Login, Override external modifications, Show Host File Name
  • Editor tab: toggle Syntax Highlighting
  • Remote tab: drag slider, verify interval label updates
  • Hotkeys tab: record/clear shortcuts for all three fields
  • Update tab: toggle automatic checks, verify last-checked date, click Check Now
  • Close and reopen Preferences — same window is reused
  • Quit and relaunch — preferences persist

2ndalpha added a commit that referenced this pull request Feb 28, 2026
Migrate the Preferences window from ObjC PreferenceController + XIB to
SwiftUI, following the same pattern established in PR #227 (URLSheet).

New SwiftUI implementation:
- PreferencesView with TabView (toolbar style) containing all 5 tabs
- RemoteIntervalMapper for slider position <-> interval mapping
- ShortcutRecorderView (NSViewRepresentable) wrapping SRRecorderControl
- SparkleObserver wrapping SUUpdater as ObservableObject
- LoginItemObserver wrapping the existing ObjC LoginItem class
- PreferencesPresenter (@objc bridge) for ApplicationController
- SF Symbol tab icons (gearshape, square.and.pencil, globe,
  command.square.fill, arrow.triangle.2.circlepath)

Deleted:
- Preferences.xib, PreferenceController.h/.m, UpdateDateTransformer.h/.m
- Unused preference tab icon PNGs (Editor, Hotkeys, Remote, Update)
@2ndalpha 2ndalpha force-pushed the swiftui-preferences branch from d93c326 to fc0085a Compare February 28, 2026 09:07
2ndalpha added a commit that referenced this pull request Feb 28, 2026
Migrate the Preferences window from ObjC PreferenceController + XIB to
SwiftUI, following the same pattern established in PR #227 (URLSheet).

New SwiftUI implementation:
- NSTabViewController with tabStyle = .toolbar for proper preference
  toolbar icons (SF Symbols: gearshape, square.and.pencil, globe,
  command.square.fill, arrow.triangle.2.circlepath)
- Individual SwiftUI tab views wrapped in NSHostingController
- RemoteIntervalMapper for slider position <-> interval mapping
- ShortcutRecorderView (NSViewRepresentable) wrapping SRRecorderControl
- SparkleObserver wrapping SUUpdater as ObservableObject
- LoginItemObserver wrapping the existing ObjC LoginItem class
- PreferencesPresenter (@objc bridge) for ApplicationController

Deleted:
- Preferences.xib, PreferenceController.h/.m, UpdateDateTransformer.h/.m
- Unused preference tab icon PNGs (Editor, Hotkeys, Remote, Update)
@2ndalpha 2ndalpha force-pushed the swiftui-preferences branch from fc0085a to 237f18f Compare February 28, 2026 09:46
2ndalpha added a commit that referenced this pull request Feb 28, 2026
Migrate the Preferences window from ObjC PreferenceController + XIB to
SwiftUI, following the same pattern established in PR #227 (URLSheet).

New SwiftUI implementation:
- NSTabViewController with tabStyle = .toolbar and toolbarStyle =
  .preference for proper macOS preference toolbar with SF Symbol icons
- Individual SwiftUI tab views wrapped in NSHostingController
- RemoteIntervalMapper for slider position <-> interval mapping
- ShortcutRecorderView (NSViewRepresentable) wrapping SRRecorderControl
- SparkleObserver wrapping SUUpdater as ObservableObject
- LoginItemObserver wrapping the existing ObjC LoginItem class
- PreferencesPresenter (@objc bridge) for ApplicationController

Deleted:
- Preferences.xib, PreferenceController.h/.m, UpdateDateTransformer.h/.m
- Unused preference tab icon PNGs (Editor, Hotkeys, Remote, Update)
@2ndalpha 2ndalpha force-pushed the swiftui-preferences branch from 237f18f to f524bfa Compare February 28, 2026 10:04
2ndalpha added a commit that referenced this pull request Feb 28, 2026
Migrate the Preferences window from ObjC PreferenceController + XIB to
SwiftUI, following the same pattern established in PR #227 (URLSheet).

New SwiftUI implementation:
- NSTabViewController with tabStyle = .toolbar and toolbarStyle =
  .preference for proper macOS preference toolbar with SF Symbol icons
- Fixed content size across all tabs to prevent window resizing on
  tab switch
- Individual SwiftUI tab views wrapped in NSHostingController
- RemoteIntervalMapper for slider position <-> interval mapping
- ShortcutRecorderView (NSViewRepresentable) wrapping SRRecorderControl
- SparkleObserver wrapping SUUpdater as ObservableObject
- LoginItemObserver wrapping the existing ObjC LoginItem class
- PreferencesPresenter (@objc bridge) for ApplicationController

Deleted:
- Preferences.xib, PreferenceController.h/.m, UpdateDateTransformer.h/.m
- Unused preference tab icon PNGs (Editor, Hotkeys, Remote, Update)
@2ndalpha 2ndalpha force-pushed the swiftui-preferences branch from f524bfa to e0a8184 Compare February 28, 2026 10:11
2ndalpha added a commit that referenced this pull request Feb 28, 2026
Migrate the Preferences window from ObjC/XIB to SwiftUI, following
the same pattern used for URLSheet in PR #227.

- Replace PreferenceController (ObjC) + Preferences.xib with a
  SwiftUI-based presenter using NSTabViewController for proper
  macOS toolbar-style tab icons
- Add 5 SwiftUI tab views: General, Editor, Remote, Hotkeys, Update
- Use SF Symbols for tab icons (gearshape, square.and.pencil, globe,
  command.square.fill, arrow.triangle.2.circlepath)
- Bridge ShortcutRecorder via NSViewRepresentable for hotkey recording
- Wrap SUUpdater and LoginItem in ObservableObject for SwiftUI binding
- Add RemoteIntervalMapper for clean slider-to-minutes conversion
- Remove unused preference icon PNGs and old ObjC files
- Add PreferencesPresenter tests (window creation, reuse, tabs, icons)
- Uniform tab content size prevents window resizing on tab switch
@2ndalpha 2ndalpha force-pushed the swiftui-preferences branch from e0a8184 to 1689fae Compare February 28, 2026 10:24
Migrate the Preferences window from ObjC/XIB to SwiftUI, following
the same pattern used for URLSheet in PR #227.

- Replace PreferenceController (ObjC) + Preferences.xib with a
  SwiftUI-based presenter using NSTabViewController for proper
  macOS toolbar-style tab icons
- Add 5 SwiftUI tab views: General, Editor, Remote, Hotkeys, Update
- Use SF Symbols for tab icons (gearshape, square.and.pencil, globe,
  command.square.fill, arrow.triangle.2.circlepath)
- Bridge ShortcutRecorder via NSViewRepresentable for hotkey recording
- Wrap SUUpdater and LoginItem in ObservableObject for SwiftUI binding
- Add RemoteIntervalMapper for clean slider-to-minutes conversion
- Remove unused preference icon PNGs and old ObjC files
- Add PreferencesPresenter tests (window creation, reuse, tabs, icons)
- Uniform tab content size prevents window resizing on tab switch
- Use Sparkle public API for reading automaticChecksEnabled instead of
  hardcoding the UserDefaults key
- Guard screenshot test against headless CI with XCTSkipIf and remove
  NSScreen.main force unwrap
@2ndalpha 2ndalpha force-pushed the swiftui-preferences branch from 101c530 to 47f3b7b Compare February 28, 2026 10:46
The rebase onto master picked up menuIcon.tiff references that were
deleted by #232 (app icon migration to asset catalog). Remove the
orphaned PBXBuildFile, PBXFileReference, and Resources build phase
entries for menuIcon.tiff and menuIcon@2x.tiff.
@2ndalpha 2ndalpha merged commit deacdc5 into master Feb 28, 2026
16 checks passed
@2ndalpha 2ndalpha deleted the swiftui-preferences branch February 28, 2026 11:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant