Search code examples
swiftswiftuitimerformatmilliseconds

Formatting milliseconds with a Timer in Swift and SwiftUI


I have this function which can successfully convert a counter variable into a beautifully formatted minutes and seconds string. How can I extend this to a format displaying MM:SS:milliseconds ?

I thought perhaps to do let milliseconds = Int(Double(seconds / 60) * 100) and append it to the return. Any tips?

Note: my timer should be publishing with enough granularity @State var timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()

func formatMmSs(counter: Double) -> String {
    let minutes = Int(counter) / 60 % 60
    let seconds = Int(counter) % 60
    return String(format: "%02i:%02i", minutes, seconds)
}

Solution

  • You just need to multiply your counter (number of seconds) times 1000, truncating remainder dividing by 1000 and coerce the result to integer:

    func formatMmSsMl(counter: Double) -> String {
        let minutes = Int((counter/60).truncatingRemainder(dividingBy: 60))
        let seconds = Int(counter.truncatingRemainder(dividingBy: 60))
        let milliseconds = Int((counter*1000).truncatingRemainder(dividingBy: 1000))
        return String(format: "%02d:%02d.%03d", minutes, seconds, milliseconds)
    }
    

    Or implemented as a computed property for BinaryFloatingPoint types:

    extension BinaryFloatingPoint {
        var intValue: Int { Int(self) }
        var minutesSecondsMilliseconds: String {
            String(format: "%02d:%02d.%03d",
                   (self / 60).truncatingRemainder(dividingBy: 60).intValue,
                   truncatingRemainder(dividingBy: 60).intValue,
                   (self * 1000).truncatingRemainder(dividingBy: 1000).intValue)
        }
    }
    

    You can also custom format the floating point. No need to coerce to integer as follow:

    extension TimeInterval {
        var minutesSecondsMilliseconds: String {
            String(format: "%02.0f:%02.0f.%03.0f",
                   (self / 60).truncatingRemainder(dividingBy: 60),
                   truncatingRemainder(dividingBy: 60),
                   (self * 1000).truncatingRemainder(dividingBy: 1000).rounded(.down))
        }
    }
    

    123.45678.minutesSecondsMilliseconds   // "02:03.456"