Accessing UIWindow from SwiftUI


Greetings, traveler!

In most SwiftUI-only projects, direct interaction with UIWindow is no longer a daily necessity. SwiftUI abstracts presentation, layout, environment, and view hierarchy in ways that rarely require developers to reach back into UIKit.

However, as soon as you introduce multiple scenes, custom overlays, advanced gesture handling, window-level presentation, or programmatic window manipulation (e.g., adjusting windowLevel for alerts), you may find yourself needing access to the underlying UIWindow.

SwiftUI does not provide a native API for this. Fortunately, UIKit still exposes the window hierarchy, and with a minimal bridging layer we can extract the window at the correct moment in the view’s lifecycle.

This article walks through one reusable pattern for retrieving a UIWindow from SwiftUI using UIViewRepresentable.

A Reusable Window Extractor for SwiftUI

The snippet below demonstrates an elegant way to observe when a SwiftUI view moves into a window and retrieve that window reference.

extension View {
    func onChangeWindow(_ perform: @escaping (UIWindow?) -> Void) -> some View {
        background {
            WindowExtractor(onExtract: perform)
        }
    }
}

This allows any SwiftUI view to attach a handler that triggers whenever the underlying window changes.

Usage:

struct ContentView: View {
    var body: some View {
        Text("Hello World!")
            .onChangeWindow { window in
                // do somehing with the window
            }
    }
}

The WindowExtractor

The core implementation is a UIViewRepresentable wrapper containing an invisible UIView subclass that listens for didMoveToWindow().

struct WindowExtractor: UIViewRepresentable {
    let onExtract: (UIWindow?) -> Void
    
    @MainActor
    final class ViewWithWindow: UIView {
        var onMoveToWindow: ((UIWindow?) -> Void)
        private weak var lastWindow: UIWindow?
        
        init(onMoveToWindow: (@escaping (UIWindow?) -> Void)) {
            self.onMoveToWindow = onMoveToWindow
            super.init(frame: .null)
            backgroundColor = .clear
            isUserInteractionEnabled = false
        }
        
        @available(*, unavailable)
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        override func didMoveToWindow() {
            super.didMoveToWindow()
            guard window !== lastWindow else { return }
            lastWindow = window
            onMoveToWindow(window)
        }
    }
    
    func makeUIView(context: Context) -> ViewWithWindow {
        ViewWithWindow(onMoveToWindow: onExtract)
    }
    
    func updateUIView(_ uiView: ViewWithWindow, context: Context) {
        uiView.onMoveToWindow = onExtract
    }
}

Key Implementation Details

  • didMoveToWindow() is the earliest reliable UIKit lifecycle call where the view is attached to a window.
  • A weak lastWindow reference ensures the handler runs only when the window actually changes.
  • The view is invisible and does not participate in hit-testing.
  • Because the UIView lives inside SwiftUI using .background(), it automatically follows view hierarchy updates.

Multi-Scene Considerations

In a multi-scene environment (e.g., an iPad app or when enabling multiple windows on macOS/iOS), each scene maintains its own window hierarchy. This extractor obtainis the exact window that SwiftUI attached the view to.

This is particularly useful when:

  • Presenting views differently depending on the scene;
  • Injecting UI that must stay inside the correct window;
  • Managing separate overlay systems per scene;
  • Coordinating window-specific services.

Because the window is delivered reactively, the API is safe even when scenes are created or destroyed dynamically.

Conclusion

SwiftUI continues to evolve, yet certain tasks still require leaning on UIKit’s primitives—especially when dealing with multi-scene applications. By embedding a minimal UIView inside SwiftUI and listening for didMoveToWindow(), developers can reliably retrieve the correct UIWindow at runtime.

This approach is lightweight, scene-aware, production-ready, and easy to integrate into any project.

If your architecture requires custom window-level interaction, custom overlays, or scene-specific behavior, this pattern provides a safe and flexible foundation.