Easy way to Format Date in SwiftUI Text

⋅ 5 min read ⋅ SwiftUI Timer Text Date

Table of Contents

Since iOS 14, SwiftUI Text has many initializers dedicated to presenting dates.

In this article, we will explore all of them.

  1. Text of Range between two dates
  2. Text of time interval
  3. Text of Date and Time
  4. Count down / up Timer
  5. Offset
  6. Relative Time

Text of Range between two dates

To present a range between two dates, we initialize a Text view with ClosedRange of two Date, e.g., startDate...endDate.

In the following example, I use dates with different time intervals to show you all the possible formats.

struct ContentView: View {
let startDate = Date.now
let secondsFromStart = Date.now.addingTimeInterval(20)
let minutesFromStart = Date.now.addingTimeInterval(20 * 60)
let hoursFromStart = Date.now.addingTimeInterval(5 * 60 * 60)
let daysFromStart = Date.now.addingTimeInterval(24 * 60 * 60)
let monthsFromStart = Date.now.addingTimeInterval(5 * 24 * 60 * 60)
let yearsFromStart = Date.now.addingTimeInterval(365 * 24 * 60 * 60)

var body: some View {
VStack {
Text(startDate...secondsFromStart)
Text(startDate...minutesFromStart)
Text(startDate...hoursFromStart)
Text(startDate...daysFromStart)
Text(startDate...monthsFromStart)
Text(startDate...yearsFromStart)
}
.font(.largeTitle)
}
}

Here is the result.

Text(startDate...endDate).
Text(startDate...endDate).

Caveat

There are two scenarios I want to highlight here.

  1. The date range from this method will not show the number of seconds.
  2. It neither show the number of years.
No seconds and years unit.
No seconds and years unit.

Based on these facts, I think we should not use this to show the range between two distance dates.

I think it best represents the date range between two dates within the same day.

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Text of time interval

Text view also accepts DateInterval, which is also a struct that represents a closed range of dates.

It also yields the same result as using ClosedRange.

struct ContentView: View {
let startDate = Date.now
let hoursFromStart = Date.now.addingTimeInterval(5 * 60 * 60)

var body: some View {
VStack {
Text(DateInterval(start: startDate, end: hoursFromStart))
Text(DateInterval(start: startDate, duration: 5 * 60))
}
.font(.largeTitle)
}
}
DateInterval.
DateInterval.

Text of Date and Time

To present date or time is easy, Text has an initializer that accepts Date and Text.DateStyle, init(_ date: Date, style: Text.DateStyle).

  • To turn a date into localized date string, we specify .date style.
  • To turn a date into localized time string, we specify .time style.
struct ContentView: View { 
let startDate = Date.now

var body: some View {
VStack {
Text(startDate, style: .date)
Text(startDate, style: .time)
}
.font(.largeTitle)
}
}
  • Text(startDate, style: .date) will show the date components of startDate.
  • Text(startDate, style: .time) will show the time components of startDate.
.date and .time Text.DateStyle.
.date and .time Text.DateStyle.

Timer

By using the .timer date style, you can have a count-down or count-up timer.

Text view will show a timer counting relative to the current date (now).

  • If the specified date is in the past, it will count up from that date.
  • If the speicfied date is in the future, it will count down to that date.
struct ContentView: View {
let pastMonth = Date.now.addingTimeInterval(-30 * 24 * 60 * 60)
let futureMonth = Date.now.addingTimeInterval(30 * 24 * 60 * 60)

var body: some View {
VStack {
// 1
Text(.distantPast, style: .timer)
Text(pastMonth, style: .timer)
Text(.now, style: .timer)
// 2
Text(futureMonth, style: .timer)
Text(.distantFuture, style: .timer)
}
.font(.largeTitle)
}
}

1 The first three Text views use the dates in the past, so the timer will count up from that date.

2 The other two will count down to the specified future dates.

.timer
.timer

Offset

By using the .offset date style, you will get an offset between the specified date and now.

You will get a +, - sign and the difference between the specified date in the largest time unit.

Example output:
+2 hours
-3 months

struct ContentView: View {
let pastMonth = Date.now.addingTimeInterval(-30 * 24 * 60 * 60)
let futureMonth = Date.now.addingTimeInterval(60 * 24 * 60 * 60)

var body: some View {
VStack {
Text(.distantPast, style: .offset)
Text(pastMonth, style: .offset)
Text(.now, style: .offset)
Text(futureMonth, style: .offset)
Text(.distantFuture, style: .offset)
}
.font(.largeTitle)
}
}
  • The date in the past will prefix with the plus sign (+).
  • The date in the future will prefix with the minus sign (-).
.offset
.offset

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Relative

By using the .relative date style, you will get a string of relative time difference between the specified date and now.

The string return using .relative show only the difference between the two dates.

That means you have no way to differentiate between the past and the future.

Example output:
2 hours, 23 minutes
1 year, 1 month

struct ContentView: View {
let pastMonth = Date.now.addingTimeInterval(-30 * 24 * 60 * 60)
let futureMonth = Date.now.addingTimeInterval(60 * 24 * 60 * 60)

var body: some View {
VStack {
Text(.distantPast, style: .relative)
Text(pastMonth, style: .relative)
Text(.now, style: .relative)
Text(futureMonth, style: .relative)
Text(.distantFuture, style: .relative)
}
.font(.largeTitle)
}
}

As you can see, there is no indicator of whether the date is in the past or future.

.relative
.relative

Compare offset, relative, and timer

Here are .offset, .relative, and .timer running side-by-side.

.offset, .relative, and .timer date style.
.offset, .relative, and .timer date style.

Read more article about SwiftUI, Timer, Text, Date, or see all available topic

Enjoy the read?

If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Buy me a coffee Tweet Share
Previous
Adding and Removing Swift Package dependencies in Xcode

Since Xcode 11, we can easily integrate Swift Package dependencies into our project. Let's learn how to do it.

Next
Swift private access level change in Swift 4

In Swift 4, there is a change in the private access level that means to end the use of fileprivate. Let's learn what it is all about.

← Home