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?
You should be able to do this. Try using:
+ (void)restoreWindowWithIdentifier:(NSUserInterfaceItemIdentifier)identifier state:(NSCoder *)state completionHandler:(void (^)(NSWindow * _Nullable, NSError * _Nullable))completionHandler NS_SWIFT_UI_ACTOR API_AVAILABLE(macos(10.7));
Appkit restores window frames automatic but if something funky is going on and that is being done too late you can ensure the window size is restored early by setting it yourself in +restoreWindowWithIdentifier:state:completionHandler: before you call the completionHandler.