Ok, some of the code was wrong.. I have just updated the playground, even adding some other formatter for the purposes of checking, that would show the time as mm:ss, but the original question remains the same, and it would be about using the MatchFormatStyle in the context of live activities.
import Foundation
import SwiftUI
import PlaygroundSupport
extension Duration {
struct MatchFormatStyle: DiscreteFormatStyle, Sendable {
let periodLength: Int
let overtime: Int
func format(_ value: Duration) -> String {
let (seconds, _): (Int64, Int64) = value.components
let current: Int = ((periodLength + overtime) * 60 + Int(seconds)) / 60
if current > periodLength {
return "\(periodLength)' (+\(current - periodLength))"
} else {
return "\(current)'"
}
}
func discreteInput(before input: Duration) -> Duration? {
let (seconds, _): (Int64, Int64) = input.components
return Duration(secondsComponent: (seconds / 60) * 60, attosecondsComponent: .zero)
}
func discreteInput(after input: Duration) -> Duration? {
let (seconds, _): (Int64, Int64) = input.components
return Duration(secondsComponent: (seconds / 60 - 1) * 60, attosecondsComponent: .zero)
}
}
}
extension FormatStyle where Self == Duration.MatchFormatStyle {
static func matchTime(currentTime: Int, periodLength: Int) -> Duration.MatchFormatStyle {
return Duration.MatchFormatStyle(periodLength: periodLength, overtime: max(currentTime - periodLength, .zero))
}
}
extension Duration {
struct MatchSecondsFormatStyle: DiscreteFormatStyle, Sendable {
let periodLength: Int
let overtime: Int
func format(_ value: Duration) -> String {
let (seconds, _): (Int64, Int64) = value.components
let current: Int = (periodLength + overtime) * 60 + Int(seconds)
return "\(String(format: "%02d", current / 60)):\(String(format: "%02d", current % 60))"
}
func discreteInput(before input: Duration) -> Duration? {
let (seconds, _): (Int64, Int64) = input.components
return Duration(secondsComponent: seconds, attosecondsComponent: .zero)
}
func discreteInput(after input: Duration) -> Duration? {
let (seconds, _): (Int64, Int64) = input.components
return Duration(secondsComponent: seconds - 1, attosecondsComponent: .zero)
}
}
}
extension FormatStyle where Self == Duration.MatchSecondsFormatStyle {
static func matchSecondsTime(currentTime: Int, periodLength: Int) -> Duration.MatchSecondsFormatStyle {
return Duration.MatchSecondsFormatStyle(periodLength: periodLength, overtime: max(currentTime - periodLength, .zero))
}
}
extension TimeDataSource<Date> {
static func matchDuration(for currentTime: Int, periodLength: Int) -> TimeDataSource<Duration> {
let minutesAhead: Double = Double(max(periodLength - currentTime, .zero))
return TimeDataSource<Date>.durationOffset(to: Date.now.addingTimeInterval(minutesAhead * 60))
}
}
struct FooView: View {
let currentTime: Int = 45
let periodLength: Int = 45
var body: some View {
Text(
TimeDataSource<Date>.matchDuration(for: currentTime, periodLength: periodLength),
format: .matchTime(currentTime: currentTime, periodLength: periodLength)
)
.frame(width: 400, height: 200)
.font(.system(size: 50))
Text(
TimeDataSource<Date>.matchDuration(for: currentTime, periodLength: periodLength),
format: .matchSecondsTime(currentTime: currentTime, periodLength: periodLength)
)
.frame(width: 400, height: 200)
.font(.system(size: 50))
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.setLiveView(FooView())