Swift
Debugging
iOS Development
Error Handling
Programming

Warning Attempt to present on whose view is not in the window hierarchy - swift

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

This UIKit warning appears when a view controller tries to present another controller before it is actually on screen, or after it has stopped being the active presenter. The code often works sometimes and fails only under timing changes, which is why the warning is so common around startup flows, async callbacks, and navigation transitions. The fix is not to suppress the warning. The fix is to present from the currently visible controller at the correct lifecycle moment.

What the Warning Really Means

When you call present(_:animated:completion:), UIKit expects the presenting controller's view to be attached to a window and actively participating in the visible hierarchy. If that is not true, UIKit warns that the presentation request is invalid.

Typical causes:

  1. Presenting from viewDidLoad.
  2. Presenting from a controller that has already been dismissed or covered.
  3. Presenting after an async callback without rechecking the current UI state.
  4. Presenting from a child controller when the container should be doing it.

The message is really saying: "the presenter you chose is not the controller currently able to present UI."

Do Not Present in viewDidLoad

viewDidLoad is too early for modal presentation because the view may exist without being attached to a window yet.

This is a common mistake:

swift
1import UIKit
2
3final class LoginViewController: UIViewController {
4    override func viewDidLoad() {
5        super.viewDidLoad()
6
7        let alert = UIAlertController(
8            title: "Welcome",
9            message: "Sign in required",
10            preferredStyle: .alert
11        )
12        alert.addAction(UIAlertAction(title: "OK", style: .default))
13
14        present(alert, animated: true)
15    }
16}

Move the presentation to a point where the controller is actually visible, usually viewDidAppear.

swift
1import UIKit
2
3final class LoginViewController: UIViewController {
4    private var hasShownAlert = false
5
6    override func viewDidAppear(_ animated: Bool) {
7        super.viewDidAppear(animated)
8
9        guard !hasShownAlert else { return }
10        hasShownAlert = true
11
12        let alert = UIAlertController(
13            title: "Welcome",
14            message: "Sign in required",
15            preferredStyle: .alert
16        )
17        alert.addAction(UIAlertAction(title: "OK", style: .default))
18
19        present(alert, animated: true)
20    }
21}

The guard avoids repeated presentation every time the screen reappears.

Recheck State After Async Work

Another common source is an async callback that finishes after the original screen is no longer active. Before presenting, verify that the controller still belongs to a window and is not already presenting something else.

swift
1DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
2    guard let self = self else { return }
3    guard self.viewIfLoaded?.window != nil else { return }
4    guard self.presentedViewController == nil else { return }
5
6    let alert = UIAlertController(
7        title: "Done",
8        message: "Upload finished",
9        preferredStyle: .alert
10    )
11    alert.addAction(UIAlertAction(title: "OK", style: .default))
12    self.present(alert, animated: true)
13}

That check matters because network completions and permission callbacks often outlive the screen that started them.

Present from the Topmost Visible Controller

In apps with tab bars, navigation controllers, or custom containers, the controller that owns the business logic may not be the one that should present. A small helper can locate the currently visible presenter.

swift
1import UIKit
2
3func topViewController(from root: UIViewController?) -> UIViewController? {
4    if let nav = root as? UINavigationController {
5        return topViewController(from: nav.visibleViewController)
6    }
7    if let tab = root as? UITabBarController {
8        return topViewController(from: tab.selectedViewController)
9    }
10    if let presented = root?.presentedViewController {
11        return topViewController(from: presented)
12    }
13    return root
14}

Used carefully, that can prevent presenting from a controller that is technically alive but no longer the active UI surface.

Keep Presentation on the Main Thread

UIKit presentation must happen on the main thread. If a background completion block tries to present directly, the timing and behavior become unreliable even if the hierarchy is otherwise valid.

When in doubt, hop back to the main queue before touching presentation code.

Debug the UI Flow, Not Just the Line That Warns

This warning is often the end result of an earlier navigation mistake. Ask:

  1. Which controller is actually visible right now.
  2. Whether a dismissal or push is still in progress.
  3. Whether the callback that presents UI belongs to the current screen.

That mindset is more useful than adding delays until the warning disappears temporarily.

Common Pitfalls

  • Presenting from viewDidLoad or another lifecycle point before the view is attached to a window.
  • Showing UI from an async callback after the original controller is no longer visible.
  • Presenting from a controller buried inside a container when the topmost visible controller should present.
  • Triggering UIKit presentation off the main thread.
  • Adding arbitrary delays instead of fixing the actual presentation timing or presenter selection.

Summary

  • The warning means the chosen presenter is not currently in the visible window hierarchy.
  • 'viewDidAppear is usually safer than viewDidLoad for first-time modal presentation.'
  • Recheck view.window and existing presentation state after async callbacks.
  • In container-heavy apps, present from the topmost visible controller.
  • Fix the UI flow itself rather than masking the problem with timing hacks.

Course illustration
Course illustration

All Rights Reserved.