This is just a little snippet that is quite useful for reporting when your GUI thread (the main thread / actor) hangs for a significant amount of time. There are numerous heavier-weight tools for analysing this sort of thing, but I’ve found that this simple monitor does what I need most of the time.
var body: some View {
SomeRootView {
…
}.task {
let approximateGranularity = Duration.milliseconds(10)
let threshold = Duration.milliseconds(50)
let clock = SuspendingClock()
var lastIteration = clock.now
while !Task.isCancelled {
try? await Task.sleep(for: approximateGranularity,
tolerance: approximateGranularity / 2,
clock: clock)
let now = clock.now
if now - lastIteration > threshold {
print("Main thread hung for ",
(now - lastIteration).formatted(.units(width: .wide,
fractionalPart: .show(length: 2))),
".",
separator: "")
}
lastIteration = now
}
}
}
You can adjust the two parameters – approximateGranularity
and threshold
– to suit your preferences. The overhead is quite tiny in CPU-usage terms, although be aware that this will cause the main thread to wake up frequently so it may have a noticeable, detrimental energy-usage impact. I suggest not deploying this to your users.
Perhaps it goes without saying, but a breakpoint set on the print
statement enables you to debug deeper into hangs. Even without that, though, it can be illuminating just to have the log message – oftentimes you don’t notice that your app is hanging, because you don’t happen to be actively interacting with it in that moment. But your users might.
Wow. That’s a nice tool. Thanks for sharing.