Creating a Money Type in Swift

The FinTech space is booming and there is a lot of buzz around development topics. So, I thought would write about how to store money using Swift. We will cover why this is even an issue in the first place and how you might address it in your code.

Decimal versus Binary Number Systems — The Heart of the Problem

Humans count things using base 10; computers find it easier to count using base 2 (1 and 0). Integers translate from base 10 to base 2 just fine. The problem arises with fractions. Not every base 10 fraction can be represented accurately when using base 2. Only base 10 fractions where the denominator represents a power of 2 integer can be accurately converted to an equivalent in base 2.

In the following example, we create a running tally of adding 0.01 (using Floats) in a loop 100 times, resulting in a sum of 1. However, we don’t get 1 due to the fact that we are using a Float (binary type), which can’t accurately represent the value 0.01.

Rounding Errors using base 2 Floats

How do we fix it? In Swift, you need to use NSDecimalNumber. Use NSDecimalNumber any time number precision is desired with decimals.

NSDecimalNumber, an immutable subclass of NSNumber, provides an object-oriented wrapper for doing base-10 arithmetic. An instance can represent any number that can be expressed as mantissa x 10^exponent where mantissa is a decimal integer up to 38 digits long, and exponent is an integer from –128 through 127.

Let’s look at how NSDecimalNumber stands up to the Float type in a couple of samples below.

Float vs NSDecimalNumber

Hopefully this makes sense. In the rest of this post, we are going to create a money type based on NSDecimalNumber and use it to create a simple currency converter.

Defining our Money Type

Our Money type needs to be an immutable object to hold both an amount and a currency type. I choose to create a struct to represent our Money type that uses an enumeration of currencies. To emphasize the connectedness of an amount and its currency, I am storing the values as a tuple within my struct. The rest of this class is defined as you might expect. There are property getters to return values, initializers to provide multiple ways to create our money, and helper functions to perform math operations. You can find the playground file with the code here.

Money struct

Rounding out our Money struct, I created an enumeration for the currencies and an enumeration for the exchange rates. We will come back to the exchange rates in a bit. For now, I want to bring your attention to how we can wrap the awkwardness of using the NSDecimalNumber class with additions to our Money type.

The NSDecimalNumber class is extremely useful, but it isn’t the easiest class to work with. Yes, there are several initializers that you can use to create an instance, but using the math operations are somewhat tedious to type. In addition, you need to create NSDecimalNumberHandlers to let the type know how to deal with rounding, overflows, and underflows. What I really want is to seamlessly use my Money type without having to worry about the underlying details of NSDecimalNumber. I want to use the operators and even be able to mix types when adding, subtracting, etc. The way to achieve this is to use operator overloading.

Operator overloading

In the code above, I overload the +operator to allow me to add two NSDecimalNumbers together in using a familiar syntax. I create additional operators to allow me to add my Money type to Floats, which can be a very handy tool to have around. I also created several other operators that you can explore on your own to reinforce their convenience and ease of creation.

Another feature we need is the ability to compare two Money types, which is accomplished using the Comparable protocol. We just need to add this to our Money struct and define an ==operator and a <operator. Swift will take care of the !=, >, <=, and >= operators for us.

Comparable protocol implementation

Creating the Currency Converter

Now that we have our Money type, we can create a currency converter to convert between currencies with which you have exchange rates. In order to complete our task, we need a way to represent an exchange rate. I created a struct to conform to the Hashable protocol because I want to store my exchange rates in a Set since it doesn’t allow duplicates, and order doesn’t matter to me. Our exchange rate struct only has three properties: CurrencyOne, CurrencyTwo, and rate. In addition, there is a computed property inverseRate to return the inverse of the exchange rate in cases where the currencies are reversed.

Exchange Rate struct implementing Hashable protocol

Our currency converter is pretty basic. I added a static Set to the Money type that holds exchange rates. To do the conversions, I created a function amountIn(currency: Currency)->Money on the Money type to check for the appropriate exchange rate in the set and then make the conversion, returning a new Money type.

Currency converter function on the Money struct

Check out the code below to see you how can use the Money struct and to also see the currency conversions in action. I implemented the CustomStringConvertible protocol to make my structs more readable in the margins.

Exercising our Money type with exchange rates

Wrapping Up

We just completed creating a Money type that uses base 10 math operations for precision and that knows its currency type. We can use our Money type like our numerical types, and we can even mix them with base 2 types like Floats. I’ll leave it to you to extend this type for your own purposes. Again, you can find the playground file here.

On a lighter note, I also wrote an article on why creating native apps is probably the best way to go in most of your mobile development endeavors. Read it and weigh in the discussion!

If you find this post helpful, recommend it for others to read. You can visit me at www.gittielabs.com and subscribe to my RSS feed so that you won’t miss a post. I’m also putting together a video course to teach Swift development and could use your input on topics you feel would be helpful. Thanks for reading!