Create a CurrencyField in SwiftUI (MINUS UIKit)

There is no need to wrap a UIKit element

You need a TextField that a user actually might want to use to enter a currency. In SwiftUI. Well, this is it.

It not only prevents you adding Characters that don’t make sense for a currency filed, but also formats your input as you go.

Read on, to find out more.

Before we start

Difficulty: Beginner | Easy | Normal | Challenging

This article has been developed using Xcode 12.5, and Swift 5.4

This article is supported by a YouTube video: https://youtu.be/Alcyyr4YAXo

Keywords and Terminology:

TextField: A control in SwiftUI that displays an editable text interface

Prerequisites:

The poor solution

Equivalent to a `UITextField` in `UIKit`, it feels like TextField is a little limited.

Many people will tell you to create a text field that only allows numbers with a decimal point (i.e. the number can be a fraction like 2.34) is to only allow the user to use the built-in decimalPad keyboard.

Something like:

TextField(“Amount”, text: $stringAmount)
.keyboardType(.decimalPad)

which would bind to an @State source of truth:

@State private var stringAmount: String = “”

*But* what if the user has a hardware keyboard attached? It turns out that the user can enter whatever they want.

The problem

Need I tell you that this is really, really bad.

Not only can the user enter in data that would need some validation later in your application (which here TextField isn’t helping us out with this) but the user should be able to enter in formatted data into the TextField. Awful.

The Solution

One solution is wrapping a `UITextField` component for use in SwiftUI, but this isn’t necessarily a great solution. If you can, you should go native.

In order to do so I’ve created a `CurrencyField.swift` file. The full code is below, but I’ll run through some of the highlights here:

**The currencyFormatter**

The currency formatter will display the currency correctly according to the user’s settings

let currencyFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
return formatter
}()

**The currencyEditingFormatter**

We have a formatter to format the input while the user is typing (which gives a good look and feel). This is different to the currencyFormatter above, since that one will fail (and potentially crash) for some user inputs.

let currencyEditingFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()

**The Properties**

The title and value are set when initialized, and the @State properties represent data stored and managed by SwiftUI.

let title: String
let value: Binding<NSNumber?>
@State private var valueWhileEditing: String = “”
@State private var isEditing: Bool = false

**The Logic**

We use a TextField, and bind this to create a two-way binding between the @State properties defined above.

Get: If we are editing display the valueWhileEditing, if not display the formattedValue.

Set: We are only interested in numbers and decimal places, which we can use to make sure we remove any extra characters from the string before updating the value. If we only have a single decimal point, we set the edited value to the stripped value and update the formatted value. If not, we might have more than one decimal place or any number of extra values, which can be removed by comparing the stripped values with the existing value length and remove the unnecessary Characters and set the valueWhileEditing and update the value.

When we have finished editing, we set the @State and update the value to present to the user.

The Callsite

We are going to bind a getter and setter property . This is very useful since we can *store* the output NSDecimalNumber from the component — awesome!

CurrencyField(
“Enter meal cost”,
value: Binding(get: {
amount.map { NSDecimalNumber(decimal: $0) }
}, set: { number in
amount = number?.decimalValue
})
)

The Complete Solution Code

This is also available in the Repo. https://github.com/stevencurtis/SwiftCoding/tree/master/SwiftUI/CurrencyField

CurrencyField.swift

ContentView.swift

Conclusion

That’s it!

We can use SwiftUI components to create rather splendid experiences for users. This is just one example — and if you want a full example using this…well, I feel another article coming on…!

If you’ve any questions, comments or suggestions please hit me up on Twitter

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store