Formatting time in a Text view in SwiftUI

Formatting time in a Text view in SwiftUI

Discover how to format data about time to be displayed in a SwiftUI app.

Rendering the data of your application on the user’s interface used to require parsing the data as text yourself, but the Text view makes our lives easier with the Text(_:format:) initializer.

The following reference shows how to use format styles to properly render currency, percentages, measurement units, dates, lists of information, person names, and how to apply inflection to text:

Formatting data as text in a Text view in SwiftUI
Learn how to format different types of data within the Text view in SwiftUI

New format styles were introduced in iOS 18 to support rendering information about time, including format style for:

  • Date Offsets
  • Date References
  • Stopwatches
  • Timers
0:00
/0:09

To see the format styles in effect you can use the following code with a TimelineView set to update every second:

struct ContentView: View {
	// Dates
    @State var startDate = Date.now
    var futureDate = Date.now.addingTimeInterval(3000)
    
    // Formatters
    let dateFormatter = Date.FormatStyle(date: .omitted, time: .standard)
    
    var body: some View {
        
        TimelineView(.periodic(from: startDate, by: 1.0)) { context in
            VStack(spacing: 12) {
                Text(context.date, format: dateFormatter)
            }
            .font(.title).bold().fontDesign(.rounded)
        }
        
    }
}

Date Offset Style

A system format to display the offset to a certain date. The offset between two dates represents how much time is in between two dates.

If the first date comes before the second date, the offset is expressed as a negative number, as in how much time is missing to reach the second date.

// Initialize the format style
var offsetFormatter: SystemFormatStyle.DateOffset {
    .offset(to: futureDate)
}
    
// Date offset style on text
Text(context.date, format: offsetFormatter)

By using a SystemFormatStyle.DateOffset format style you can parse date information as text easily. The initializer init(to:allowedFields:maxFieldCount:sign:) gives you the flexibility to define the units of time that may be used to express the offset and the number of fields that can be shown.

Date Reference Style

A system format style to refer to a date in the most natural way. Depending on the distance between the dates the final result differs. Short timeframes are referred to in a relative way, while longer timeframes will be referred to in an absolute way.

// Initialize the format style
var referenceFormatter: SystemFormatStyle.DateReference {
    .reference(to: futureDate)
}

// Date reference style on text
Text(context.date, format: referenceFormatter)

By using a SystemFormatStyle.DateReference format style you can parse the distance between two dates in the most natural way possible. The initializer init(to:allowedFields:maxFieldCount:thresholdField:) allows further customization of the final result. With the thresholdField you can define when the representation of the date will be relative or absolute.

Stopwatch Style

The representation of time as the system stopwatch.

// Initialize the format style
var stopwatchStyle: SystemFormatStyle.Stopwatch {
    .stopwatch(startingAt: startDate)
}

// Stopwatch style on text
Text(context.date, format: stopwatchStyle)

By using the SystemFormatStyle.Stopwatch format style the final result will look like the system stopwatch. The initializer init(startingAt:showsHours:maxFieldCount:maxPrecision:) allows you to define the precision of the stopwatch visualization and the maximum number of fields displayed at once.

Timer Style

The representation of time counting up or down from a given interval.

// Initialize the format style
var timerUpStyle: SystemFormatStyle.Timer {
    .timer(countingUpIn: startDate..<futureDate)
}

// Timer going up style on text
Text(context.date, format: timerUpStyle)

// Initialize the format style
var timerDownStyle: SystemFormatStyle.Timer {
    .timer(countingDownIn: startDate..<futureDate)
}

// Timer going down style on text
Text(context.date, format: timerDownStyle)

By using the SystemFormatStyle.Timer format style interval between the two dates will be parsed with the amount of time remaining or the time left between them.

The initializer init(countingDownIn:showsHours:maxFieldCount:maxPrecision:) will parse the data counting down, from the maximum amount of time left towards zero. The initializer init(countingUpIn:showsHours:maxFieldCount:maxPrecision:) will do the opposite, counting from zero toward the maximum amount of time in between them.