- Published on
- 3 min read
> Showing a relative "Time ago" date in SwiftUI
Displaying dates as "5 minutes ago" or "yesterday" instead of raw timestamps makes your app feel more conversational and easier to scan. Foundation's RelativeDateTimeFormatter handles this out of the box, with full localization support and several formatting styles to match your UI's tone.
Basic Usage
The formatter compares two dates and produces a human-readable string describing their relationship:
let pastDate = Date.now.addingTimeInterval(-3600) // 1 hour ago
let formatter = RelativeDateTimeFormatter()
let relative = formatter.localizedString(for: pastDate, relativeTo: Date.now)
// "1 hour ago"
For future dates, the formatter automatically adjusts the phrasing:
let futureDate = Date.now.addingTimeInterval(86400) // 24 hours from now
formatter.localizedString(for: futureDate, relativeTo: Date.now)
// "in 1 day"
Customizing the Output Style
The formatter has two main properties that control its output: unitsStyle and dateTimeStyle.
The unitsStyle property determines how units are displayed. The default is .full, but you have several options:
let date = Date.now.addingTimeInterval(-7200) // 2 hours ago
formatter.unitsStyle = .full
// "2 hours ago"
formatter.unitsStyle = .short
// "2 hr. ago"
formatter.unitsStyle = .abbreviated
// "2 hr. ago"
formatter.unitsStyle = .spellOut
// "two hours ago"
The dateTimeStyle property controls whether the formatter uses numeric values or named references like "yesterday". The default is .numeric:
let yesterday = Date.now.addingTimeInterval(-86400)
formatter.dateTimeStyle = .numeric
// "1 day ago"
formatter.dateTimeStyle = .named
// "yesterday"
Named style works well for recent dates where terms like "yesterday" or "last week" feel natural. For older dates or when you need consistency, numeric style is more predictable.
Handling Capitalization
The formattingContext property handles capitalization based on where the text appears in a sentence:
formatter.formattingContext = .beginningOfSentence
// "In 5 minutes"
formatter.formattingContext = .middleOfSentence
// "in 5 minutes"
formatter.formattingContext = .standalone
// Default context
This is particularly useful when you're building sentences dynamically or displaying the relative time in different UI contexts.
Using It in SwiftUI
Here's a simple Text view that displays a relative timestamp:
struct TimeAgoText: View {
let date: Date
private var relativeString: String {
let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .full
return formatter.localizedString(for: date, relativeTo: Date.now)
}
var body: some View {
Text(relativeString)
}
}
For timestamps that should update automatically, you can use a timer to refresh the view:
struct LiveTimeAgoText: View {
let date: Date
@State private var now = Date.now
private let timer = Timer.publish(every: 60, on: .main, in: .common).autoconnect()
private var relativeString: String {
let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .full
return formatter.localizedString(for: date, relativeTo: now)
}
var body: some View {
Text(relativeString)
.onReceive(timer) { _ in
now = Date.now
}
}
}
Localization
The formatter automatically respects the user's locale settings, but you can override it if needed:
let formatter = RelativeDateTimeFormatter()
formatter.locale = Locale(identifier: "es")
formatter.localizedString(for: date, relativeTo: Date.now)
// "hace 2 horas"
This makes RelativeDateTimeFormatter ideal for apps that support multiple languages. You get properly localized relative times without maintaining translation tables for time units.
When to Use Something Else
RelativeDateTimeFormatter works best for recent dates within the past few months. For older dates, the output becomes less useful ("52 weeks ago" isn't particularly helpful). Consider falling back to a standard date format for anything older than a reasonable threshold, or use a combination approach where recent items show relative times and older items show actual dates.
// Continue_Learning
Building Custom Container Views in SwiftUI with Subviews
iOS 18 introduced public APIs for building custom container views that can decompose content into individual subviews. Learn how to use ForEach(subviews:), Group(subviews:), and ContainerValues.
ControlWidgetButton for Control Center Widgets
Create interactive buttons for the Control Center using ControlWidgetButton and App Intents in iOS 18.
Sensory Feedback and Haptics in SwiftUI
Add haptic feedback to your SwiftUI views using the sensoryFeedback modifier, with built-in support for success, error, impact, and selection feedback.
// Stay Updated
Get notified when I publish new tutorials on Swift, SwiftUI, and iOS development. No spam, unsubscribe anytime.