How can I reliably get the final restored window size on macOS when onAppear / viewDidAppear fires too early?

I’m running into a macOS window restoration behavior issue where viewDidAppear (AppKit) or onAppear (SwiftUI) fires before the window’s final restored size is applied.

AppKit example

class MyViewController: NSViewController {
    override func viewDidLayout() {
        print("viewDidLayout: \(view.bounds.size)")
    }
    override func viewDidAppear() {
        print("viewDidAppear: \(view.bounds.size)")
    }
}

Logs on launch:

viewDidAppear: (480.0, 270.0)
viewDidLayout: (480.0, 270.0)
viewDidLayout: (556.0, 476.0)
viewDidLayout: (556.0, 476.0)

The correct restored size is (556.0, 476.0), but viewDidAppear initially reports the old default size (480.0, 270.0).


SwiftUI equivalent

struct MyView: View {
    var body: some View {
        GeometryReader { geo in
            VStack {}
                .onAppear {
                    print("onAppear: \(geo.size)")
                }
                .onChange(of: geo.size) {
                    print("onChange: \(geo.size)")
                }
        }
    }
}

Logs on launch:

onAppear: (900.0, 450.0)
onChange: (680.0, 658.0)

Problem

I need to run some setup code:

  • Only once
  • After the view/window has its correct restored size
  • Without rerunning on every layout or geometry change

Question

What is the proper macOS-native way to perform one-time startup logic only after the final restored window size is available?

Is there a recommended lifecycle hook or pattern for this?

Also, is it expected behavior that onAppear / viewDidAppear reports the pre-restoration size, or is it a bug?

How can I reliably get the final restored window size on macOS when onAppear / viewDidAppear fires too early?
 
 
Q