Popover rendering unnecessarily

I'm seeing an issue where a popover driven by a TCA binding is rendered twice when it appears.

Here's the simplest example I could come up with: Popover.swift · GitHub

When the popover opens, POPOVER RENDER prints twice. Creating the same type of example with a @State var isPopoverVisible, the string only prints once, so this seems to be something I'm doing wrong with TCA, or maybe a bug in the library or binding.

Any ideas?

Unfortunately this is a bug with vanilla SwiftUI. While it doesn't affect @State, it does affect @ObservedObject:

import SwiftUI

class Model: ObservableObject {
  @Published var open = false
}

struct ContentView: View {
  @ObservedObject var model = Model()

  var body: some View {
    VStack {
      Button {
        model.open = true
      } label: {
        { () -> EmptyView in
          print("PARENT RENDER")
          return EmptyView()
        }()
        Text("Open popover")
      }
    }
    .padding()
    .popover(isPresented: self.$model.open) {
      { () -> EmptyView in
        print("POPOVER RENDER")
        return EmptyView()
      }()
      Text("Popover")
    }
  }
}

It might be worth filing a feedback with Apple, but also at the end of the day I'm not sure how much one can depend on the number of times various views are computed. The fact that these views are recomputed a few extra times may not actually matter in practice.

Also note that a view recomputing its body doesn't necessarily correspond to a "view rendering" in any significant sense.

1 Like

Thanks, good to know that it's not user error, and good point on the body recomputing not being a major issue.

I thought this was causing a MapKit map inside my popover to flash / re-render, but it seems like that's actually not related to the view body recomputing, but a flaw in MapKit's SwiftUI implementation :confused:

Side note that isn't relevant to your question: Instead of this to execute a side effect (printing) in a view builder:

{ () -> EmptyView in
  print("PARENT RENDER")
  return EmptyView()
}()
Text("Open popover")

You can also write this:

let _ = print("PARENT RENDER")
Text("Open popover")
1 Like