10

I'm converting my iOS13 app for iPadOS to SceneDelegate (multi window).

How can I get the current UIWindow from the current SceneDelegate?

I know that a can access the current scene using UIView.window or UIViewController.view.window, but I have a non UI class (AppDelegate) where I need to get the window (keyWindow until iOS12) to show a snack bar on top of everything.

I used to do [UIApplication sharedApplication].keyWindow but now, of course, that's wrong.

7 Answers 7

13

Swift 5.x / iOS 13.x

Inspired by many solutions, to get back my old window property (searching it inside the connectedScenes) I've realized a little extension:

extension UIApplication {
    var currentWindow: UIWindow? {
        connectedScenes
        .filter({$0.activationState == .foregroundActive})
        .map({$0 as? UIWindowScene})
        .compactMap({$0})
        .first?.windows
        .filter({$0.isKeyWindow}).first
    }
}

Usage:

if let window = UIApplication.shared.currentWindow { 
   // do whatever you want with window
}
5
  • i think on ios13 when windows are side by side... both are "foregroundActive", but i can't confirm.
    – Fabiosoft
    Commented Nov 2, 2019 at 22:42
  • yes, I can confirm this, still have no clue how to access the active (or who click the applink) Commented Nov 25, 2019 at 3:21
  • @JeffersonSetiawan What about removing .first from the last line, and adding this line to the end the extension : .filter({$0.isFocused == true}).first. Can you try this one? Commented Nov 26, 2019 at 8:01
  • does not working using .filter({$0.isFocused == true}).first
    – Max
    Commented Nov 9, 2020 at 10:18
  • 1
    You can collapse .map and .compactMap with .compactMap { $0 as? UIWindowScene }?
    – z2k
    Commented Jul 17, 2021 at 2:20
7

You'll want to simply iterate through all your windows and find the key window manually.

for (UIWindow *window in [UIApplication sharedApplication].windows) {
    if (window.isKeyWindow) {
        // you have the key window
        break;
    }
}

DO NOT use UISceneActivationStateForegroundActive as the check. That means something different and folks are introducing bugs by using logic to find the first UISceneActivationStateForegroundActive window scene.

0
2

It sounds like you probably want to move this logic to your SceneDelegate.

The SceneDelegate now has knowledge of whether the window is connected to the scene, so it might make sense to have every connected scene listen to whatever event is driving the snack bar, and the show it on its window. This would then result in every visible window showing the snack bar (1 or more).

1

You could try:

    if let window = UIApplication.shared.windows.first(where: { (window) -> Bool in window.isKeyWindow}) {
    //your code
    }

Hope this helps!

0

Now you have more than one window, one for each scene. First, you have to answer which one you need at the moment of usage.

Probably you want to get the window of the currently active scene then you can use this:

    UIWindow* window = nil;
    if (@available(iOS 13.0, *))
    {
        for (UIWindowScene* wScene in [UIApplication sharedApplication].connectedScenes)
        {
            if (wScene.activationState == UISceneActivationStateForegroundActive)
            {
                window = wScene.windows.firstObject;

                break;
            }
        }
    }
4
  • this is incorrect. That is not what UISceneActivationStateForegroundActive means. You want window.isKeyWindow Commented Sep 17, 2019 at 16:22
  • The question is "What is the way to get UIWindow from non-UI class. On the other hand, the key window is: "The key window receives keyboard and other non-touch related events. Only one window at a time may be the key window." You can have more than one active windows that are touch-able at the same time on the screen and only one of them is keyWindow, but it might not be the wanted window. There is no absolutely correct answer to this question, that is why my solution is a proposal in one concrete scenario. Commented Sep 17, 2019 at 17:49
  • And that is why Apple deprecated, keyWindow property from UIApplication. Commented Sep 17, 2019 at 17:59
  • 1
    I see what you were getting at. I suppose in a world of rapid stack overflow copying and pasting I wanted to interject to avoid folks misunderstanding and applying code that will lead to bugs (as my developer did copying this code to our code base). I apologize for pushing that your intent was wrong, that was unfair. Commented Sep 17, 2019 at 22:00
0

For SwiftUI, Tested in iOS 16+

extension UIApplication {
    var currentWindow: UIWindow? {
        connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .first?
            .windows
            .first
    }
}

It's usage.

guard let anchor = UIApplication.shared.currentWindow else {
   fatalError("Window did not found in Hierarchy")
}
-2

I haven't tried this yet, but you should be able to get all the windows with [UIApplication sharedApplication].windows and then pick if you want to show the snack bar on all of the windows or one window.

2
  • With this method you get an array of windows...but you don’t know what’s the current
    – Fabiosoft
    Commented Aug 24, 2019 at 13:43
  • This has been deprecated since iOS 15.0
    – S.Moore
    Commented Nov 1, 2023 at 18:24

Not the answer you're looking for? Browse other questions tagged or ask your own question.