Amount Formatter

A class capable of formatting numeric amounts to strings using Swift's NumberFormatter

Available from v6.5.16

NumberFormatting

NumberFormatting is a protocol that defines how numeric amounts are formatted into strings. This protocol is used throughout the design system for formatting currencies, percentages, and other numeric values, with special support for the InputAmount component.


Platform availability: iOS 17.0+

When to use:

  • Use NumberFormatting when you need to format monetary amounts, percentages, or other numeric values with locale-aware formatting.
  • Consider custom implementation only for simple, non-monetary number formatting outside the design system.

Import


                                                        
                                                        
                                                            import BackbaseDesignSystem
                                                        import Foundation
                                                        
                                                            

Overview

NumberFormatting is a protocol with two main methods:

  • format(amount:options:): Formats a Decimal amount into a formatted string. Used primarily for displaying formatted amounts as plain text (e.g., in labels).
  • inputAmountFormat(parameters:): Formats a raw input string for display in an InputAmount component. This method handles validation, locale-specific separators, right-to-left number conversion, and currency alignment.

The design system includes a default implementation (DesignSystem.Formatting.NumberFormatter) that uses Swift's NumberFormatter under the hood. You can customize formatting behavior by:

  1. Configuring DesignSystem.Formatting.Options (recommended for most cases)
  2. Providing a custom formatter that conforms to NumberFormatting (for advanced use cases)

Protocol Methods

format(amount:options:)

Formats the given amount with the provided options. Used mainly for formatting currencies shown as plain text.
Method Signature:


                                                        
                                                        
                                                            func format(amount: Decimal, options: DesignSystem.Formatting.Options) -> String?
                                                        
                                                            


Parameters:

  • amount: The numerical amount to be formatted
  • options: Formatting options such as locale, formatting style (currency, percent, etc.), and so on

Returns: A formatted string representation of the amount, or nil if formatting fails
Usage Example:


                                                        
                                                        
                                                            var options = DesignSystem.Formatting.Options()
                                                        options.locale = Locale(identifier: "en_US") // Only set if you really need something specific, defaults to `.autoupdatingCurrent`
                                                        options.formattingStyle = .currencyISOCode
                                                        options.showsPlusSign = true
                                                        options.abbreviator = DesignSystem.shared.formatting.abbreviators.default
                                                        
                                                        let formatter: NumberFormatting = DesignSystem.Formatting.makeFormatter()
                                                        
                                                        let formattedAmountString = formatter.format(amount: 1200, options: options)
                                                        // Result: "+USD 1.2k"
                                                        
                                                            

inputAmountFormat(parameters:)

Formats an input amount string for display in an amount input field. This method processes a raw amount string entered by the user, validates it, handles locale-specific decimal and grouping separators, converts right-to-left numbers if needed, determines currency alignment, and returns formatted data suitable for display in an InputAmount component.
Method Signature:


                                                        
                                                        
                                                            func inputAmountFormat(parameters: InputAmountFormatParameters) -> InputAmountFormatOutput
                                                        
                                                            


Parameters:

  • parameters: An InputAmountFormatParameters instance containing:
    • The raw amount string entered by the user
    • The currency code
    • Formatting options
    • The validator instance

Returns: An InputAmountFormatOutput instance containing:

  • formattedAmountNoCurrency: The formatted amount string (without currency symbols/codes)
  • decimalAmount: The decimal representation of the amount
  • formattingOptions: The formatting options used
  • currencyAlignment: Preferred currency alignment (leading or trailing)
  • validationResult: Validation status
Note: Changing the formatting options object will affect the InputAmount component's layout potentially. For instance:
- If the formattingStyle is changed from currency to currencyISOCode, the currency symbol will be shown as the ISO code.
- If the customSymbol is changed, the currency symbol will be shown as the custom symbol.
The journey using the InputAmount component may be setting some of these formatting options, so it's important to consider this when changing the formatting options, as you may inadvertently override the journey's default behavior by overriding the inputAmountFormat function.

Data Structures

InputAmountFormatParameters

A data structure containing the parameters for formatting an input amount string.
Initializer:


                                                        
                                                        
                                                            public init(
                                                            amount: String,
                                                            currencyCode: String,
                                                            options: DesignSystem.Formatting.Options,
                                                            validator: InputAmountValidator
                                                        )
                                                        
                                                            

InputAmountFormatOutput

A data structure containing the result of formatting an input amount string.
Initializer:


                                                        
                                                        
                                                            public init(
                                                            formattedAmountNoCurrency: String,
                                                            decimalAmount: Decimal?,
                                                            formattingOptions: DesignSystem.Formatting.Options,
                                                            currencyAlignment: InputAmount.CurrencyAlignment? = nil,
                                                            validationResult: Result<Void, InputAmountValidator.Error>
                                                        )
                                                        
                                                            

Default Implementation

The protocol includes a default implementation of inputAmountFormat(parameters:) that:

  1. Validates the input using the provided validator
  2. Normalizes locale-specific decimal and grouping separators
  3. Converts right-to-left numbers to English numerals if needed
  4. Determines currency symbol position based on locale
  5. Formats the amount and removes currency symbols/codes for display

The formatted result is suitable for display in an InputAmount component where the currency is shown separately from the amount.

Custom Implementation

For advanced use cases, you can create a custom formatter that conforms to NumberFormatting. Custom formatters can:

  • Override specific formatting behaviors
  • Override currency symbol position detection
  • Modify right-to-left number conversion
  • Change validation behavior
  • Apply custom formatting logic

Basic Custom Formatter Example


                                                        
                                                        
                                                            class CustomAmountFormatter: NumberFormatting {
                                                            let defaultFormatter = DesignSystem.Formatting.makeFormatter()
                                                        
                                                            func format(amount: Decimal, options: DesignSystem.Formatting.Options) -> String? {
                                                                // Use the default formatter but with custom logic
                                                                return defaultFormatter.format(amount: amount, options: options)
                                                            }
                                                            
                                                            func inputAmountFormat(parameters: InputAmountFormatParameters) -> InputAmountFormatOutput {
                                                                // Use the default implementation
                                                                return defaultFormatter.inputAmountFormat(parameters: parameters)
                                                            }
                                                        }
                                                        
                                                        // Use custom formatter
                                                        let customFormatter = CustomAmountFormatter()
                                                        DesignSystem.shared.formatting.amountFormatter = customFormatter
                                                        
                                                            

Advanced Custom Formatter Example

Here's an example that overrides currency alignment and formatting style:


                                                        
                                                        
                                                            class AdvancedAmountFormatter: NumberFormatting {
                                                            var forceCurrencyAlignment: InputAmount.CurrencyAlignment = .leading
                                                            var forceISOFormat: Bool = false
                                                            let defaultFormatter = DesignSystem.Formatting.makeFormatter()
                                                            
                                                            func format(amount: Decimal, options: DesignSystem.Formatting.Options) -> String? {
                                                                var modifiedOptions = options
                                                                
                                                                // Force ISO format if needed
                                                                if forceISOFormat {
                                                                    modifiedOptions.formattingStyle = .currencyISOCode
                                                                }
                                                                
                                                                return defaultFormatter.format(amount: amount, options: modifiedOptions)
                                                            }
                                                            
                                                            func inputAmountFormat(parameters: InputAmountFormatParameters) -> InputAmountFormatOutput {
                                                                var modifiedOptions = parameters.options
                                                                
                                                                // Force ISO format if needed
                                                                if forceISOFormat {
                                                                    modifiedOptions.formattingStyle = .currencyISOCode
                                                                }
                                                                
                                                                // Create modified parameters
                                                                let modifiedParameters = InputAmountFormatParameters(
                                                                    amount: parameters.amount,
                                                                    currencyCode: parameters.currencyCode,
                                                                    options: modifiedOptions,
                                                                    validator: parameters.validator
                                                                )
                                                                
                                                                // Use default formatter with modified parameters
                                                                var result = defaultFormatter.inputAmountFormat(parameters: modifiedParameters)
                                                                
                                                                // Override currency alignment
                                                                result = InputAmountFormatOutput(
                                                                    formattedAmountNoCurrency: result.formattedAmountNoCurrency,
                                                                    decimalAmount: result.decimalAmount,
                                                                    formattingOptions: result.formattingOptions,
                                                                    currencyAlignment: forceCurrencyAlignment,
                                                                    validationResult: result.validationResult
                                                                )
                                                                
                                                                return result
                                                            }
                                                        }
                                                        
                                                        // Usage
                                                        let formatter = AdvancedAmountFormatter()
                                                        formatter.forceCurrencyAlignment = .trailing
                                                        formatter.forceISOFormat = true
                                                        DesignSystem.shared.formatting.amountFormatter = formatter
                                                        
                                                            

How Custom Formatters Affect InputAmount

When you provide a custom NumberFormatting implementation and set it via DesignSystem.shared.formatting.amountFormatter, all InputAmount components will use your custom formatter. This affects:

Currency Display

  • Currency Symbol/Code: The formattingStyle in the returned InputAmountFormatOutput determines whether the currency is shown as a symbol (e.g., "$") or ISO code (e.g., "USD")
  • Custom Symbols: If customSymbol is set in the formatting options, it will be displayed instead of the default currency symbol
  • Currency Position: The currencyAlignment property in the output determines whether the currency appears before or after the amount

Formatting Behavior

  • Decimal Places: Controlled by minFractionDigits and maxFractionDigits in the formatting options
  • Grouping Separators: Determined by the locale and usesGroupingSeparator option
  • Validation: The validationResult in the output determines whether the input is valid and if the user can type more characters into the input

Integration with InputAmount

The InputAmount component uses the NumberFormatting protocol internally:

  1. When text is entered, InputAmount calls inputAmountFormat(parameters:) with the raw input string
  2. The formatter processes the input and returns an InputAmountFormatOutput
  3. InputAmount uses the output to:
    • Display the formatted amount (without currency)
    • Show the currency symbol/code separately
    • Position the currency (leading or trailing) based on currencyAlignment
    • Show validation errors based on validationResult
    • Update formatting options if they were modified

Best Practices

  1. Prefer Formatting Options: For most use cases, customize formatting through DesignSystem.Formatting.Options rather than creating a custom formatter
  2. Preserve Default Behavior: When creating custom formatters, consider using the default implementation and only overriding specific behaviors
  3. Test Locale-Specific Behavior: Ensure your custom formatter handles different locales correctly, especially for decimal and grouping separators, remember the keyboard's locale is always .autoupdatingCurrent and not the locale you may override on the formatting options.
  4. Consider Validation: Don't bypass validation unless you have a specific reason. The default implementation includes important validation checks

Accessibility

This utility is built with accessibility in mind. See the information below for supported behaviors and configuration options to ensure a fully accessible experience for all users.

Accessibility configuration

This utility does not have direct accessibility properties. When displaying formatted amounts in UI components, configure accessibility on the containing view.

Best practices

  • Use appropriate accessibilityLabel values for formatted currency strings.
  • Avoid abbreviations in accessibility labels to ensure VoiceOver reads amounts correctly.
  • Consider using separate formatting options for display and accessibility labels.

Dependencies

  • External dependencies: None
  • Internal dependencies: DesignSystem.Formatting.Options, InputAmountValidator

Design tokens

This utility does not use design tokens. It provides formatting logic that works with any styling configuration.

See also

  • InputAmount - Component that uses NumberFormatting for input formatting