2

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.

Anton Tropashko
  • 5,486
  • 5
  • 41
  • 66
  • I'm not seeing the model of your code... on the ForEach struct you have to remember that this is not a for-each in swift code, it's a View protocol conformance, so, when you refer model.sectionsModel, this could be triggering the View redraw over and over. That's my guess. If you can share the whole project (PoC) to me to run here I could get some more clues. The same for model.carouselModels. I guess both are @Published on your viewModel. – Allan Garcia Mar 12 '23 at 13:41
  • That part got taken care of switching over to Lazy[HV]Stack. But there are animation hitches remaining somewhere. The project belongs to my employer sadly can;t share it. – Anton Tropashko Mar 13 '23 at 12:20
  • The best way to overcome this is to produce a Proof of Concept, where the "hitches" still appear and put here beside your question. Because LazyVStack if some fetch are happening on the rendering of the cell could lead to hitches if those fetches are made in the Main Queue. – Allan Garcia Mar 16 '23 at 20:52

0 Answers0