I do not get a helpful "This method should not be called on the main thread as it may lead to UI unresponsiveness." purple warning from Xcode and yet the UI is extremely sluggish. How would I track the source of that?
In my naivete I added some code to plug myself into runloop like so
private var previousTimeInSeconds: Double = 0
private var previousLogInSeconds: Double = 0
private lazy var displayLink: CADisplayLink = {
let displayLink = CADisplayLink(target: self,
selector: #selector(displayLoop))
return displayLink;
}()
var watchdog: Timer?
private func startLoop() {
previousTimeInSeconds = displayLink.timestamp
displayLink.add(to: .current, forMode: .common)
}
@objc private func displayLoop() {
let currentTimeInSeconds = displayLink.timestamp
let elapsedTimeInSeconds = currentTimeInSeconds - previousTimeInSeconds
previousTimeInSeconds = currentTimeInSeconds
//let actualFramesPerSecond = 1 / (displayLink.targetTimestamp - displayLink.timestamp) // is showing constant 59.xxx FPS
let actualFramesPerSecond = 1 / elapsedTimeInSeconds
if currentTimeInSeconds - previousLogInSeconds > 1 {
NSLog("fps: \(actualFramesPerSecond)")
previousLogInSeconds = currentTimeInSeconds
}
watchdog?.invalidate()
let interval = TimeInterval(0.5)
watchdog = Timer.scheduledTimer(withTimeInterval: interval, repeats: false) { [weak self] _ in
NSLog("iOS-299 main loop did not get control back in \(interval) second")
}
}
but NSLog("iOS-299 main loop did not get control back in..") does not trip while I see delays in a few seconds after switching tabs in my ui.
In my particular case ScrollView over a VStack with a few dozen items was not helping. Switching over to List naturally helped to some extent
var body: some View {
List {
// ScrollView {
// VStack(spacing: 0.0) {
SomeHeavyweightHorizontalType1(models: model.carouselModels)
vvvvvv bottleneck is somewhere here vvvvvvvvv
ForEach(model.sectionsModel, id: \.title) { section in
let _ = NSLog("iOS-299 section \(section.title)")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ but this log prints twice. Why?
SomeHeavyweightHorizontalListType2(model: section)
}
// }
}
UPD: In my case the job was done by code review and replacing HStack in SomeHeavyweightHorizontalListType2 with LazyHStack (that was wrapped in a VStack that I ended up changing to LazyVStack) but I wonder what's the instruments driven approach to this would have been if I did not end up tracking this down by codereview?
Partial answer from the authoritative source "Explore UI animation hitches and the render loop" https://developer.apple.com/videos/play/tech-talks/10855/
I ran Apple "Hitches" instrument cluster and on my particular case all high severity hitch types are "pre-commit(s) latency" However the backtraces are pretty useless cause they end up in the innards of CoreAnimation, UIKit and SwiftUI. No code of mine shows up in the traces.
I guess the hitchy code has to be rewritten in UIKit.