Skip to content

Mac SongDetailView is ~740 lines larger than iOS — extract inline subviews and share filtering logic #108

@dprodger

Description

@dprodger

Context

While wrapping up architecture review P2 #3 (shared view-model extraction for the three detail view pairs), we noticed that apps/Mac/Views/SongDetailView.swift is 1,626 lines versus apps/iOS/Views/SongDetailView.swift at 887 lines. That's ~740 lines of asymmetry between two files that are supposed to render the same data.

The difference breaks down into two independent causes.

Cause 1: iOS extracts subviews into component files; Mac inlines everything

iOS keeps these subviews in their own files under apps/iOS/Components/:

File Lines
RecordingsSection.swift 479
BackingTracksSection.swift 304
TranscriptionsSection.swift 186
TranscriptionRowView.swift 116
Total 1,085

Mac defines the same material inline at the bottom of SongDetailView.swift:

Type Lines
RecordingCard ~215
FeaturedRecordingCard ~191
TranscriptionRow ~83
BackingTrackRow ~99
StreamingButtons ~58
Total ~646

Cause 2: Mac's main SongDetailView struct is itself ~290 lines bigger

Mac's struct has recording filtering + grouping logic that iOS delegates to its RecordingsSection component:

  • availableInstruments(_:) — Mac/SongDetailView.swift:477
  • filteredRecordings(_:) — :493
  • groupedRecordings(_:) — :539
  • groupByDecade(_:) — :549
  • groupByArtistWithConsolidation(_:) — :574

Plus the corresponding Mac-only filter UI state: selectedFilter, selectedVocalFilter, selectedInstrument.

These represent a Mac-only UX choice (decade grouping and artist consolidation) that iOS does not currently offer, so it is not purely "code in the wrong place" — but the asymmetry means a logic fix has to be made twice if iOS ever adopts the same grouping.

Proposed fix

  1. Pure file moves first (low-risk): extract Mac's five inline subviews into apps/Mac/Components/ mirroring the iOS structure.

    • RecordingCard.swift
    • FeaturedRecordingCard.swift
    • TranscriptionRow.swift
    • BackingTrackRow.swift
    • StreamingButtons.swift
    • Expected result: Mac SongDetailView.swift drops to ~980 lines. No behavior change. Diff reviewable line-by-line.
  2. Then share the filtering/grouping logic: move availableInstruments / filteredRecordings / groupedRecordings / groupByDecade / groupByArtistWithConsolidation into a shared utility (apps/Shared/Support/RecordingGrouping.swift, or methods on SongDetailViewModel). This is the higher-value step because it kills the "every grouping bug fix has to be made twice" risk if iOS ever adopts this UX.

  3. Consider exposing decade/artist grouping in iOS too, now that the logic is shared. This is a product decision, not a cleanup one — skip if not desired.

Context links

  • Architecture review item that spawned this observation: doc/architecture-review-2026-04.md (P2 Maybe cache Wikipedia search actions not just pages #3)
  • Refactor that set up the shared-view-model pattern: commits 58a1149, 903d4a3, f0955e5
  • apps/Shared/ViewModels/SongDetailViewModel.swift is a natural home for any logic that should be platform-agnostic

Not in scope for this issue

  • The Mac-only filter UI itself (picker rows, etc.) can stay in SongDetailView.swift — it is legitimately platform-specific layout.
  • Changing any actual rendering behavior on either platform. This is purely structural.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions