diff --git a/Sources/RepoPrompt/App/RepoPromptApp.swift b/Sources/RepoPrompt/App/RepoPromptApp.swift index b0f36dd64..240aaa903 100644 --- a/Sources/RepoPrompt/App/RepoPromptApp.swift +++ b/Sources/RepoPrompt/App/RepoPromptApp.swift @@ -69,6 +69,12 @@ struct RepoPromptApp: App { // Avoid process-killing SIGPIPE when the child closes stdin while we're still writing. signal(SIGPIPE, SIG_IGN) + // Pre-warm date formatter cache on a background thread to avoid blocking the main + // thread with ICU locale processing (ulocimp_addLikelySubtags) on first render. + Task.detached(priority: .utility) { + MessageTimestampFormatter.warmUp() + } + SentryTelemetryBootstrap.start() ProcessDebugLogging.log( diff --git a/Sources/RepoPrompt/Infrastructure/UI/Components/MessageTimestampFormatter.swift b/Sources/RepoPrompt/Infrastructure/UI/Components/MessageTimestampFormatter.swift index 4a19f3f06..5c0e63e2c 100644 --- a/Sources/RepoPrompt/Infrastructure/UI/Components/MessageTimestampFormatter.swift +++ b/Sources/RepoPrompt/Infrastructure/UI/Components/MessageTimestampFormatter.swift @@ -6,6 +6,21 @@ import Foundation enum MessageTimestampFormatter { private static let cache = MessageTimestampFormatterCache() + /// Pre-warms the formatter cache on a background thread so that the first call from + /// the main thread (during SwiftUI rendering) does not block on expensive ICU locale + /// processing (ulocimp_addLikelySubtags / setLocalizedDateFormatFromTemplate). + static func warmUp() { + let calendar = Calendar.current + let locale = Locale.current + let now = Date() + // Touch every format / template variant the formatter can produce. + _ = cache.string(from: now, format: "HH:mm:ss", calendar: calendar, locale: locale) + for template in ["EEE", "MMM d", "MMM d y"] { + _ = cache.string(from: now, template: template, calendar: calendar, locale: locale) + } + _ = cache.relativeDayLabel(-1, calendar: calendar, locale: locale) + } + static func string( from date: Date, includeDateContext: Bool,