How to combine updating of Text(date, style: .relative) with styling of formatter:

Consider the code below...

The green box using style: .relative is updating the relative time continuously, but it has too much detail for my taste.
The red box using formatter: rFormatter has exactly the right detail but it doesn't update unless I force it to - maybe a timer or something?

How do I most efficiently combine the formatting I want with the continuous updating?

Code Block
struct ContentView: View {
    var relDate: Date {
        let comps = Calendar.current.dateComponents([.hour, .day, .month, .year], from: Date())
        return Calendar.current.date(from: comps)!
    }
    let rFormatter = RelativeDateTimeFormatter()
    init() {
        rFormatter.dateTimeStyle = .named
    }
    var body: some View {
        VStack {
            Text(relDate, style: .relative)
                .padding()
                .background(Color(.green))
            Text(relDate, formatter: rFormatter)
                .padding()
                .background(Color(.red))
        }
    }
}


Answered by OOPer in 667652022

maybe a timer or something?

I'm not sure if it would be the most efficient or not, but using Timer is one way that actually works.
Code Block
struct ContentView: View {
var relDate: Date {
let comps = Calendar.current.dateComponents([.hour, .day, .month, .year], from: Date())
return Calendar.current.date(from: comps)!
}
let rFormatter: RelativeDateTimeFormatter = {
let formatter = RelativeDateTimeFormatter()
formatter.dateTimeStyle = .named
return formatter
}()
let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
@State var relDateText = ""
var body: some View {
VStack {
Text(relDate, style: .relative)
.padding()
.background(Color(.green))
Text(relDateText)
.padding()
.background(Color(.red))
}
.onReceive(timer, perform: {_ in
self.relDateText = rFormatter.string(for: relDate)!
})
}
}


Accepted Answer

maybe a timer or something?

I'm not sure if it would be the most efficient or not, but using Timer is one way that actually works.
Code Block
struct ContentView: View {
var relDate: Date {
let comps = Calendar.current.dateComponents([.hour, .day, .month, .year], from: Date())
return Calendar.current.date(from: comps)!
}
let rFormatter: RelativeDateTimeFormatter = {
let formatter = RelativeDateTimeFormatter()
formatter.dateTimeStyle = .named
return formatter
}()
let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
@State var relDateText = ""
var body: some View {
VStack {
Text(relDate, style: .relative)
.padding()
.background(Color(.green))
Text(relDateText)
.padding()
.background(Color(.red))
}
.onReceive(timer, perform: {_ in
self.relDateText = rFormatter.string(for: relDate)!
})
}
}


Thank you for your reply. That's a good start and I'll use it for now. Maybe later I can think of a way to increase the timer intervals - like when the update was hours ago then I'll set the interval to 1 minute or something.

Strange: in Swift Playgrounds it doesn't work (the timer doesn't fire at all, but the relDateText is set to the initial interval.) Probably a quirk of Playgrounds, I guess.

In Xcode Previews it only works in "live" preview which is as expected. Couldn't find a quick way to fix that other than hardcode an initial value instead of "".

In Simulator it works quite well. I didn't try onDevice yet but why would it fail there?

Couldn't find a quick way to fix that other than hardcode an initial value instead of "".

You can re-write the initial value in init():
Code Block
init() {
_relDateText = State(initialValue: rFormatter.string(for: relDate)!)
}


why would it fail there?

Where is there?
How to combine updating of Text(date, style: .relative) with styling of formatter:
 
 
Q