The amount-entry suite (Components/EnterAmount/) plus standalone amount renderers.
These power Send / Convert / DashDEX-style screens.
File Components/EnterAmount/EnterAmountView.swift · @available(iOS 14, macOS 11, *)
The top-level amount-entry control. Two layouts selected by EnterAmountStyle:
.single(default) — Max button +SwapAmountView+DashPickerViewcurrency list..dualSwap— two amounts (e.g. Dash + fiat) stacked; tapping animates them swapping the large/small slots. Optional Max button and a trailing input-type switcher.
Each layout has its own initializer (the .single init keeps existing call sites
source-compatible).
@State private var value = "12.5"
@State private var currency: CurrencyOption = .dash
EnterAmountView(
value: $value,
selectedCurrency: $currency,
options: [.fiat("USD"), .dash, .coin("BTC")],
onMax: { /* fill max */ },
onCurrencyTap: { /* e.g. open fiat picker */ },
onPaste: { /* paste from clipboard */ }
)EnterAmountView(
primaryAmount: "1.23456", // host-formatted numeric string
secondaryAmount: "150.00", // host-formatted numeric string
primaryCurrency: .dash,
secondaryCurrency: .fiat("USD"),
isPrimarySelected: true, // true → primary in large slot
isCurrencySelectorHidden: false, // hides the chevron next to secondary
currencyCodes: ["DASH", "USD"], // non-nil → trailing vertical switcher
selectedCurrencyCode: "DASH",
onMax: { }, // nil → no Max button (amount stays centered)
onSwap: { /* toggle isPrimarySelected */ },
onCurrencyTap: { },
onPaste: { },
onSelectInputType: { code in /* user picked a code */ }
)For the dual-swap API, primaryAmount and secondaryAmount are numeric or
host-formatted strings only. The renderer adds the currency symbol from
primaryCurrency / secondaryCurrency.
The dual-swap layout uses three columns — leading (Max or spacer), centered animated amount stack, trailing (switcher or spacer) — with equal fixed side widths so the amount stays truly centered regardless of which controls are present. All amounts are strings the host formats; the component never does currency math.
File Components/EnterAmount/SwapAmountView.swift · @available(iOS 14, macOS 11, *)
The amount display used inside EnterAmountView. Renders a large amount (optional
currency symbol + number + Dash logo) with optional top/bottom helper text and an optional
secondary line. Public, so it can be used standalone.
SwapAmountView(
amount: "1.5",
symbol: nil, // e.g. "$" for fiat; nil for Dash
secondaryText: "$ 150.00",
topText: "Enter amount",
bottomText: nil,
showDashLogo: true,
showCurrencyButton: false, // chevron beside the amount → onCurrencyTap
onCurrencyTap: nil,
onPaste: nil // long-press to paste
)Two modes:
- Static (
swapAnimationKey == nil): single amount + optional secondary line. Drives the.singlestyle. - Animated dual-swap (
swapAnimationKey: Bool): aZStackof two persistent rows (A = primary, B = secondary). When the key flips, both rows animatescaleEffect+offsettogether for a continuous grow-rise / shrink-fall, with the font snapping to the target weight and a compensating scale bridging the jump.DualSwapAmountView(internal) is the thin wrapperEnterAmountViewuses; tapping the container firesonSwap.
Display normalization: empty → "0"; a bare-decimal input gets a leading zero
(.34 → 0.34). It does not reformat or cap precision — that's the host's job.
scaleToFitWidth keeps the whole amount group on one line, shrinking uniformly when long
(see Utilities).
The .dashPasteContextMenu(onPaste:) helper (in this file) wires long-press-to-paste via
onLongPressGesture — deliberately not .contextMenu, which breaks inside a
UIHostingController.
File Components/DashAmount.swift · @available(iOS 14, macOS 11, *)
Renders a duffs Int64 (1 DASH = 100,000,000 duffs) as a localized decimal amount
followed by the Dash currency glyph. Display-only.
DashAmount(
amount: 6_791_000, // duffs
fontSize: 15,
weight: .medium,
dashSymbolFactor: 1, // glyph size = fontSize × factor
sign: .negativeOnly
)DashAmountSign — .none (never show a sign) · .negativeOnly (minus only;
default) · .always (+/-). Zero never shows a sign in any mode.
Sentinels: amount == .max or .min renders the localized "Not available" string
(used for unknown/pending balances). Formatting: up to 5 fraction digits, grouping
separators, current locale.
File Components/EnterAmount/DashBalanceView.swift · @available(iOS 14, macOS 11, *)
Trailing balance block: a symbol-free, pre-formatted Dash amount string + Dash glyph,
with an optional fiat sub-line beneath. Unlike DashAmount, it takes already-formatted
strings (from a view model), not duffs.
DashBalanceView(balance: "1.5", fiat: "$ 150.00") // fiat nil → hide sub-lineFile Components/ReceiveEstimateView.swift · @available(iOS 14, macOS 11, *)
A centered estimate line shown beneath amount inputs. It shows a loading spinner first,
then an error message, then the title + estimated amount. When fiat is provided it is
joined with ≈; when approximate is true and no fiat is present, the amount is
prefixed with ~.
ReceiveEstimateView(
isVisible: true,
title: "You will receive",
amount: "0.5 DASH",
isLoading: false,
errorMessage: nil,
approximate: false
)The view collapses to nothing when isVisible is false.
File Components/EnterAmount/DashPickerView.swift · @available(iOS 14, macOS 11, *)
A compact vertical inline picker over any Hashable option; the selected row gets a
subtle highlighted background. Generic and reusable.
@State private var selected: CurrencyOption = .dash
DashPickerView(
options: [.fiat("USD"), .dash, .coin("BTC")],
title: { $0.displayName }, // (Option) -> String
selected: $selected
)In .single EnterAmountView this is the currency list on the trailing side.
File Components/EnterAmount/CurrencyOption.swift · public Hashable enum
Models the three input kinds the amount controls understand:
public enum CurrencyOption: Hashable {
case fiat(String) // currency code, e.g. "USD"
case dash
case coin(String) // coin code, e.g. "BTC"
}Helpers: isFiat, isCoinInput, displayName ("DASH" / the code), and symbol
(fiat → locale currency symbol via the internal DashCurrencySymbol; dash → nil, shown
as a logo instead; coin → the code). Convenience values like .dash / .fiat("USD") are
used as defaults in EnterAmountView.