Formatting dates in Swift using Date.FormatStyle on iOS 15

Published on: May 27, 2022

Working with dates isn’t easy. And showing them to your users in the correct locale hasn’t always been easy either. With iOS 15, Apple introduced a new way to convert Date objects from and to String. This new way comes in the form of the new Formatter api that replaces DateFormatter.

As any seasoned iOS developer will tell you, DateFormatter objects are expensive to create, and therefor kind of tedious to manage correctly. With the new Formatter api, we no longer need to work with DateFormatter. Instead, we can ask a date to format itself based on our requirements in a more performant, easier to use way.

In this post I will show you how you can convert Date objects to String as well as how you can extract a Date from a String.

Converting a Date to a String

The most straightforward way to convert a Date to a String is the following:

let formatted = Date().formatted() // 5/26/2022, 7:52 PM

By default, the formatted() function uses a compact configuration for our String. The way formatted() converts our Date to String takes into account the user’s current locale. For example, if my device was set to be in Dutch, the date would be formatted as 26-5-2022 19:54 which is a more appropriate formatting for the Dutch language.

However, this might not always be what we need. For example, we might want to have our date formatted as May 26 2022, 7:52 PM. We can use the following code to do that:

let formatted = Date().formatted(
    .dateTime
        .day().month(.wide).year()
        .hour().minute()
)

Let’s break this code apart a bit. The formatted function takes an object that conforms to the FormatStyle protocol as its argument. There are various ways for us to create such an object. The FormatStyle protocol has several convenient extensions that can provide us with several different formatters.

For example, when sending a Date to a server, we’ll often need to send our dates as ISO8601 compliant strings. Before I explain the code you just saw, I want to show you how to grab an ISO8601 compliant string from the current Date.

let formatted = Date().formatted(.iso8601) // 2022-05-26T18:06:55Z

Neat, huh?

Okay, back to the example from before. The .datetime formatter is used as a basis for our custom formatting. We can call various functions on the object that’s returned by the .datetime static property to select the information that we want to show.

Some of these properties, like the month, can be configured to specify how they should be formatted. In the case of .month, we can choose the .wide formatting to spell out the full month name. We could use .narrow to abbreviate the month down to a single letter, or we could use one of the other options to represent the month in different ways.

If you omit a property, like for example .year(), our formatted date will omit the year that’s embedded in the Date. And again, the underlying formatter will always automatically respect your user’s locale which is really convenient.

Another way to format the date is to by specifying how you want the date and time to be formatted respectively:

let formatted = Date().formatted(date: .complete, time: .standard) // Thursday, May 26, 2022, 8:15:28 PM

The above provides a very verbose formatted string. We can make a more compact one using the following settings:

let formatted = Date().formatted(date: .abbreviated, time: .shortened) // May 26, 2022, 8:16 PM

It’s even possible to omit the date or time entirely by using the .omitted option:

let formatted = Date().formatted(date: .abbreviated, time: .omitted) // May 26, 2022

There are tons of different combinations you could come up with so I highly recommend you explore this api some more to get a sense of how flexible it really is.

Creating a Date from a String

Converting String to Date is slightly less convenient than going from a Date to a String but it’s still not too bad. Here’s how you could cover the common case of converting an ISO8601 compliant string to a Date:

let string = "2022-05-26T18:06:55Z"
let expectedFormat = Date.ISO8601FormatStyle()
let date = try! Date(string, strategy: expectedFormat)

We make use of the Date initializer that takes a string and a formatting strategy that’s used to parse the string.

We can also use and configure an instance of FormatStyle to specify the components that we expect to be present in our date string and let the system parse it using the user’s locale:

let string = "May 26, 2022, 8:30 PM"
let expectedFormat = Date.FormatStyle()
    .month().year().day()
    .hour().minute()
let date = try! Date(string, strategy: expectedFormat)

The order of our date components doesn’t matter; they will automatically be rearranged to match the user’s locale. This is super powerful, but it does mean that we can’t use this to parse dates on devices that use a different locale than the one that matches the string’s locale. The best locale agnostic date string is ISO8601 so if you have control over the date strings that you’ll parse, make sure you use ISO8601 when possible.

Summary

In this short article, you learned how you can use iOS 15’s FormatStyle to work format Date objects. You saw how to go from Date to String, and the other way around. While FormatStyle is more convenient than DateFormatter, it’s iOS 15 only. So if you’re still supporting iOS 14 you’ll want to make sure to check out DateFormatter too.

Categories

Swift

Subscribe to my newsletter