Skip to content

feat: add DashIcon enums, SwitchView and dashFont line heights - #8

Merged
romchornyi merged 6 commits into
feat/menu-icon-assetsfrom
feat/dash-icon-and-switch
Jul 29, 2026
Merged

feat: add DashIcon enums, SwitchView and dashFont line heights#8
romchornyi merged 6 commits into
feat/menu-icon-assetsfrom
feat/dash-icon-and-switch

Conversation

@romchornyi

@romchornyi romchornyi commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Stacked on the asset import. Base this on that PR, not master — retarget to
master once it merges. Split out of #6 so CodeRabbit can actually review it: with the
asset catalog in the same PR the diff was 125 reviewable files against a limit of 100,
and the review was skipped entirely. This half is 20.

Issue being fixed or feature implemented

Every icon in the library was referenced by raw string, so a renamed asset failed
silently at runtime with a blank image instead of a build error — and three call sites
were already broken that way.

Text was styled with .font(Font.dash.X), which applies the font but leaves each Text
at the system's natural leading instead of the documented design line height.

The switch component was a native Toggle with a tint, which cannot match a design whose
thumb is a 39×24 pill rather than a circle. DashSwitch carried a TODO saying exactly
that.

What was done?

DashIcon — typed asset names

Foundation/Icon_DashUI.swift adds a namespace of String-backed enums mirroring the
asset catalog structure: 137 icons in 12 groups. The raw value is the imageset name, so a
wrong name is now a compile error. DashIconAsset gives each case .source (a
DashIconSource, for components that take one) and .image (a plain Image, styled by
the caller). Group prefixes drop out of case names since the enum already namespaces
them — menu-account-error reads as DashIcon.Menu.accountError.

Generated from the catalog on disk rather than hand-written, and CaseIterable so a
gallery preview can enumerate a group.

All 20 raw asset strings in the library were replaced. Three were already broken:

Call site Problem
SystemMessageView default icon still pointed at warning_triangle, renamed during normalization. This is a default parameter in shipping code, not a preview, so every SystemMessageView built without an explicit icon rendered blank.
MenuItem preview referenced menu-receive.disabled; the imageset is menu-receive-disabled
TransactionView passed transaction-mining with no bundle:, so it resolved against the host app's bundle rather than the library's

dashFont — line heights applied

21 unambiguous .font(Font.dash.X) call sites become .dashFont(.X), applying the
documented line height alongside the font. Two preview labels move off Apple's
.subheadline onto .dashFont(.subhead).

17 sites deliberately stay on .font:

  • The previews in LineHeight+DashUI and DashTextStyle exist precisely to contrast
    bare .font against .dashFont. Converting them would erase what they demonstrate.
  • SearchBar uses Font.dash.footnote.weight(.semibold); the nearest token,
    footnoteMedium, is .medium, so converting would change the weight.
  • DashButtonSize.fontSize returns .system(size: 16/14/13/12). 16/13/12 map onto
    callout/footnote/caption1, but 14pt has no token, so a partial conversion
    would leave the sizes inconsistent.
  • DashAmount, NumericKeyboardView and SwapAmountView size text dynamically or take
    a Font parameter. Converting those means reshaping private signatures to pass
    DashTextStyle, and in SwapAmountView the value is animated between states, so
    adding line height would affect layout. That is a separate change, not a mechanical
    one.

SwitchView

Implements the switch from the iOS design system (node 1841:259): a 64×28 track with a
39×24 pill thumb inset by 2, sliding 21pt with a 0.2s ease-in-out. Disabled is read from
the isEnabled environment value rather than a parameter, so .disabled(true) on any
ancestor works.

Track colors map onto design variables exactly — gray300Alpha50 is
switch/track-fill-off, blue is switch/track-fill-on, black1000Alpha20 is
switch/track-fill-off-disabled.

.coderabbit.yaml

Filters **/*.xcassets/** out of review so future icon imports do not blow past the file
limit and skip the review of the source changes.

How Has This Been Tested?

swift build passes at every commit.

The refactors were verified by grep rather than by eye: zero remaining raw .custom("…")
names outside the generated file, and the 17 surviving .font( sites are exactly the
ones listed above. Icon_DashUI.swift is generated from the catalog on disk, so every
raw value is guaranteed to name a real imageset.

SwitchView ships previews for all four states plus an interactive one.

Not tested: the library is not yet consumed by dashwallet-ios, so this is library-side
only.

Breaking Changes

None. DashIcon is additive and SwitchView is new. DashSwitch is untouched.

Worth a decision rather than a break: the design system has no
track-fill-on-disabled variable, so a disabled on switch currently uses the same
colour as a disabled off switch — only the thumb position tells them apart. Needs
confirming with the designer.

Also unresolved: menu-connections has no dark artwork because it is absent from Figma's
Menu (dark) frame, and DashIcon.Menu.sendDisabled exists but nothing references it —
the MenuItem preview only shows the receive variant.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation

Summary by CodeRabbit

  • New Features

    • Added a customizable switch control with on/off states, enabled/disabled styling, animation, and accessibility support.
    • Added a strongly typed icon library for consistent access to DashUIKit artwork.
  • Enhancements

    • Updated address, amount, picker, search, menu, transaction, and message components with refreshed icon assets.
    • Standardized typography across interactive controls and financial views for more consistent visual presentation.
    • Refined success and error illustrations with the updated artwork set.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@romchornyi, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 35 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 846c7cf4-605b-47f2-8d94-70e5eed3db0d

📥 Commits

Reviewing files that changed from the base of the PR and between 640298f and 3f868e2.

📒 Files selected for processing (1)
  • Sources/DashUIKit/Components/Switch/SwitchView.swift
📝 Walkthrough

Walkthrough

DashUIKit adds a typed icon asset API, migrates existing components to typed icons and .dashFont(...), and introduces a bindable SwiftUI SwitchView with enabled-state styling, animation, accessibility traits, and previews.

Changes

DashUIKit UI updates

Layer / File(s) Summary
Typed icon asset API
Sources/DashUIKit/Foundation/Icon_DashUI.swift
Adds DashIconAsset and grouped DashIcon enums with typed asset names, DashIconSource values, and SwiftUI images.
Typed icon adoption
Sources/DashUIKit/Components/AddressFieldView.swift, Sources/DashUIKit/Components/EnterAmount/*, Sources/DashUIKit/Components/SearchBar.swift, Sources/DashUIKit/Components/Illustrations/*, Sources/DashUIKit/Components/MenuItem.swift, Sources/DashUIKit/Components/SystemMessageView.swift, Sources/DashUIKit/Components/Transaction/TransactionView.swift
Replaces custom icon construction and sources with DashIcon assets while preserving surrounding view behavior.
Dash typography modifier migration
Sources/DashUIKit/Button/DashButton.swift, Sources/DashUIKit/Components/AddressFieldView.swift, Sources/DashUIKit/Components/EnterAmount/*, Sources/DashUIKit/Components/NumericKeyboardView.swift, Sources/DashUIKit/Components/SearchBar.swift, Sources/DashUIKit/Foundation/LineHeight+DashUI.swift
Replaces Font.dash values applied through .font(...) with .dashFont(...) modifiers in views and previews.
Bindable switch component
Sources/DashUIKit/Components/Switch/*
Adds ThumbView and public SwitchView, including bound toggling, state-dependent track colors, animation, accessibility traits, and debug previews.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SwitchView
  participant BindingBool
  User->>SwitchView: Tap switch hit area
  SwitchView->>BindingBool: Toggle isOn
  BindingBool-->>SwitchView: Updated isOn value
  SwitchView->>SwitchView: Animate track and thumb state
Loading

Possibly related PRs

  • dashpay/DashUIKit#3: Adds the illustration components whose image rendering is updated here to use typed DashIcon.Common assets.
  • dashpay/DashUIKit#5: Adds related components and overlaps with the typography and typed-icon migrations in this PR.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main additions: typed DashIcon enums, SwitchView, and dashFont-related typography updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/dash-icon-and-switch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Implements the switch from the iOS design system (node 1841:259) as a
standalone SwiftUI view: a 64x28 track with a 39x24 pill thumb inset by 2,
sliding 21pt between states with a 0.2s ease-in-out.

Covers all four states. Disabled is read from the isEnabled environment value
rather than a parameter, so .disabled(true) on any ancestor works:

  on  + enabled   blue
  on  + disabled  blueAlpha50
  off + enabled   gray300Alpha50
  off + disabled  black1000Alpha20

The off and on colors map exactly to the switch/track-fill-off and
switch/track-fill-on design variables. The design system has no
track-fill-on-disabled variable, so blueAlpha50 is a stand-in and needs
confirming with the designer.
Introduces DashIcon, a namespace of String-backed enums mirroring the asset
catalog folder structure — 137 icons across 12 groups. The raw value is the
imageset name, so a wrong name becomes a compile error instead of a blank
image at runtime.

DashIconAsset gives each case .source (a DashIconSource, for components that
take one) and .image (a plain Image, styled by the caller). Group prefixes are
dropped from case names since the enum already namespaces them:
menu-account-error reads as DashIcon.Menu.accountError.

Generated from the catalog rather than hand-written, and CaseIterable so a
gallery preview can enumerate a group.
Replaces all 20 raw asset-name strings in the library with DashIcon cases.

Three of them were already broken by the catalog normalization and were
failing silently:

  - SystemMessageView's default icon still pointed at "warning_triangle",
    renamed to system-message-warning-triangle. This is a default parameter
    value in shipping code, not a preview, so every SystemMessageView built
    without an explicit icon rendered a blank one.
  - MenuItem's preview used "menu-receive.disabled"; the imageset is now
    menu-receive-disabled.
  - TransactionView passed "transaction-mining" with no bundle, so it
    resolved against the host app's bundle rather than the library's.

Call sites that only needed an Image collapse from
Image(dash: .custom(name, bundle: .dashUIKit)) to DashIcon.Group.case.image.
Converts every unambiguous .font(Font.dash.X) to .dashFont(.X), so the
documented design line height is applied alongside the font rather than
leaving each Text at the system's natural leading. Also maps two preview
labels off Apple's .subheadline onto .dashFont(.subhead).

Deliberately left on .font:

  - The previews in LineHeight+DashUI and DashTextStyle exist precisely to
    contrast bare .font against .dashFont — converting them would erase what
    they demonstrate.
  - SearchBar's Font.dash.footnote.weight(.semibold): the nearest token,
    footnoteMedium, is .medium, so converting would change the weight.
  - DashButtonSize.fontSize returns .system(size: 16/14/13/12); 14pt has no
    token, so a partial conversion would leave the sizes inconsistent.
  - DashAmount, NumericKeyboardView and SwapAmountView size text dynamically
    or take a Font parameter. Converting those means reshaping the API to pass
    DashTextStyle, which is a separate change.
The design system has no track-fill-on-disabled variable, so the disabled on
state was a stand-in. Aligns it with the disabled off state, which means a
disabled switch reads the same either way and only the thumb position tells
them apart.
@romchornyi
romchornyi force-pushed the feat/dash-icon-and-switch branch from 113e9ae to 640298f Compare July 29, 2026 13:18
@romchornyi

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
Sources/DashUIKit/Foundation/Icon_DashUI.swift (1)

30-43: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Expose conversions through the protocol contract.

source and image are unavailable to callers holding any DashIconAsset or T: DashIconAsset, because they exist only on the RawRepresentable<String>-constrained extension. Declare them as protocol requirements with these default implementations, or narrow the public protocol/documentation to assetName only.

Proposed API shape
 public protocol DashIconAsset {
     /// The imageset name in the asset catalog.
     var assetName: String { get }
+    var source: DashIconSource { get }
+    var image: Image { get }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/DashUIKit/Foundation/Icon_DashUI.swift` around lines 30 - 43, Update
the DashIconAsset protocol to declare source and image requirements, then retain
their existing default implementations in the
RawRepresentable<String>-constrained extension so callers using any
DashIconAsset or T: DashIconAsset can access both conversions.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/DashUIKit/Components/Switch/SwitchView.swift`:
- Around line 28-45: Update the public SwitchView accessibility configuration
after its existing gesture to expose toggle semantics: add a meaningful
accessibility label, report isOn as "On" or "Off", use the .isToggle trait
instead of only .isButton, and provide an accessibility action that toggles
isOn. Keep the existing visual styling and tap behavior unchanged.

---

Nitpick comments:
In `@Sources/DashUIKit/Foundation/Icon_DashUI.swift`:
- Around line 30-43: Update the DashIconAsset protocol to declare source and
image requirements, then retain their existing default implementations in the
RawRepresentable<String>-constrained extension so callers using any
DashIconAsset or T: DashIconAsset can access both conversions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6082152e-e040-45be-875e-b763293f4144

📥 Commits

Reviewing files that changed from the base of the PR and between ead5ef8 and 640298f.

📒 Files selected for processing (19)
  • Sources/DashUIKit/Button/DashButton.swift
  • Sources/DashUIKit/Components/AddressFieldView.swift
  • Sources/DashUIKit/Components/DashAmount.swift
  • Sources/DashUIKit/Components/EnterAmount/DashBalanceView.swift
  • Sources/DashUIKit/Components/EnterAmount/DashPickerView.swift
  • Sources/DashUIKit/Components/EnterAmount/DualSwapAmountView.swift
  • Sources/DashUIKit/Components/EnterAmount/EnterAmountView.swift
  • Sources/DashUIKit/Components/EnterAmount/SwapAmountView.swift
  • Sources/DashUIKit/Components/Illustrations/ErrorIllustration.swift
  • Sources/DashUIKit/Components/Illustrations/SuccessIllustration.swift
  • Sources/DashUIKit/Components/MenuItem.swift
  • Sources/DashUIKit/Components/NumericKeyboardView.swift
  • Sources/DashUIKit/Components/SearchBar.swift
  • Sources/DashUIKit/Components/Switch/Components/ThumbView.swift
  • Sources/DashUIKit/Components/Switch/SwitchView.swift
  • Sources/DashUIKit/Components/SystemMessageView.swift
  • Sources/DashUIKit/Components/Transaction/TransactionView.swift
  • Sources/DashUIKit/Foundation/Icon_DashUI.swift
  • Sources/DashUIKit/Foundation/LineHeight+DashUI.swift

Comment thread Sources/DashUIKit/Components/Switch/SwitchView.swift
SwitchView announced itself as a button with no value, and the tap gesture was
not exposed as an accessibility action, so VoiceOver users could not tell the
state or change it.

Adds a localized On/Off value and an explicit accessibility action. The trait
becomes .isToggle where available; that API is iOS 17 and this component must
stay usable on iOS 14, so the older path keeps .isButton.

No accessibility label is set. SwitchView takes no title, so it has no context
to describe itself with — a generic label would also shadow the one the host
sets, the same way SwiftUI's own Toggle requires the caller to supply it.
@romchornyi
romchornyi merged commit 60ef387 into feat/menu-icon-assets Jul 29, 2026
2 checks passed
@romchornyi
romchornyi deleted the feat/dash-icon-and-switch branch July 29, 2026 13:44
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.

2 participants