Text formatting in Swift

Daniel Sumara
4 min readApr 9, 2017

In almost every app there comes time when we have to make some formatting. Some time we need to convert Bool into readable string, more often Date object into text literal which will be understandable to people who use our application, not saying about rounding numbers to two spaces after coma/dot sign (depending of country or OS settings) or placing separator between thousandths parts of number.

Apple come across this requirements and create sets of formatters which we can consume in our applications. Intension of Apple developer was creating very clear API, which witch will be country independent but also very customizable. They make very good job with this. For example to create DateFormatter to print Date without time in single label on screen we have to:

var date: Date = //... 2001-01-02func setupDate() {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .mediumStyle
dateFormatter.timeStyle = .noStyle
dateLabel.text = dateFormatter.string(from: date)
}

This will display different strings depend on iOS or OS X language preferences. If we use English as our default language this will display Jan 2, 2001, for French we will get 2 janv. 2001 and for Japanese we get 2001/01/02 .

Rest of formatters have very similar API. We can use enums to define behavior depending on language preferences, but we also can use string formats to create system configuration independent formatter. When we send date to REST Service as string we can define our formatter as:

func getCurrentDateStringForRest() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return dateFormatter.string(from: Date())
}

We have very good and useful tool in *Formatter classes, but creating new instance of formatter whenever we needed one can be frustrated. More over DRY is broken, because we repeating exactly same code to create formatter in different places of our application. To solve this problem lot of programmers creates singletons, utilities classes or static properties in viewModels. If it leads to remove duplicated code it is good. If it leads to reduce of created instances of formatters it is even better.

I decided to create my formatting utility in Swift way, using structs and extensions.

First step was creating container for my formatters. I end up with:

One file with single structure Formatters which will be container for concrete formatters. If you like you can change struct to enum to get Phantom type (It is type which can’t create any instances — you can achieve same result creating struct with private init).
In the very same directory I create another file called Formatters+Numbers.swift where I put:

In extension for Formatter struct/enum I create another struct/enum for formatters related with Numbers with three properties (static properties are lazy by default) representing my formatters and sets of methods for changing numbers into well formatted strings. Values passed to methods could be optional, in that case I return default value if client define one or empty string. Intelisense for xCode resolve this into:

I create one formatter per one result type. Apple docs says Formatter classes are thread safe, but if we are using one instance in lot of threads much better is use unmutable objects. If you need more overloads, just define it. Business of mine required only that ones, so I decide to not implement more over that I need. It makes my code simpler and cleaner.

Beyond API to formatting numbers I also need formatters for Date objects. So I create file called Formatters+Dates.swift as:

Business required formatting independent of OS (hardcoded strings formats), so I create extra Format enum with strings I need.

As you can see API is very easy to learn and very clear. Moreover, tapping on format case with option key will show us comment/documentation if one don’t know which format should be used:

My last formatter type is Boolen. I need convert true or false into “Yes” or “No” strings. So I create another file Formatters+Boolen.swift with single method:

Intelisense from xCode display my formatter as image show.

My formatters are grouped in Formatter namespace. My code is grouped in directory.

Pros:

  • Formatters are grouped by types
  • Very clear API
  • Thread safe
  • No god class — each type have own file with code base
  • Easy to extend
  • Easy to test (we didn’t mutate formatters)

We can do more extension :) Let’s extend our UILabel to use our new code:

And we can use it in client code as:

func set(balance: Double) {
balanceLabel.text(from: balance) // 13 234,56
}

--

--