279

I'm using Core Data with Cloud Kit, and have therefore to check the iCloud user status during application startup. In case of problems I want to issue a dialog to the user, and I do it using UIApplication.shared.keyWindow?.rootViewController?.present(...) up to now.

In Xcode 11 beta 4, there is now a new deprecation message, telling me:

'keyWindow' was deprecated in iOS 13.0: Should not be used for applications that support multiple scenes as it returns a key window across all connected scenes

How shall I present the dialog instead?

Leo Dabus
  • 216,610
  • 56
  • 458
  • 536
Hardy
  • 3,510
  • 2
  • 15
  • 23
  • Are you doing this in `SceneDelegate` or `AppDelegate`? And, could you post a bit more code so we can duplicate? –  Jul 21 '19 at 15:38
  • 2
    There is no 'keyWindow' concept in iOS anymore as a single app can have multiple windows. You could store the window you create in your `SceneDelegate` (if you are using `SceneDelegate`) – Sudara Jul 22 '19 at 03:34
  • 2
    @Sudara: So, if I have no view controller yet, but want to present an alert - how to do it with a scene? How to get the scene, so that its rootViewController can be retrieved? (So, to make it short: what is the Scene equivalent to the "shared" for UIApplication?) – Hardy Jul 22 '19 at 12:20

23 Answers23

380

Edit The suggestion I make here is deprecated in iOS 15. So now what? Well, if an app doesn't have multiple windows of its own, I presume the accepted modern way would be to get the first of the app's connectedScenes, coerce to a UIWindowScene, and take its first window. But that is almost exactly what the accepted answer does! So my workaround feels rather feeble at this point. However, I'll let it stand for historical reasons.


The accepted answer, while ingenious, might be overly elaborate. You can get exactly the same result much more simply:

UIApplication.shared.windows.filter {$0.isKeyWindow}.first

I would also caution that the deprecation of keyWindow should not be taken overly seriously. The full warning message reads:

'keyWindow' was deprecated in iOS 13.0: Should not be used for applications that support multiple scenes as it returns a key window across all connected scenes

So if you are not supporting multiple windows on iPad there is no objection to going ahead and continuing to use keyWindow.

matt
  • 485,702
  • 82
  • 818
  • 1,064
  • How would you handle a segue like this ```let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "homeVC") as! UITabBarController UIApplication.shared.keyWindow?.rootViewController = vc``` because with iOS 13 and the card view this becomes a problem because a user after say logging out will get pushed to the login screen with the main app in the view hierarchy where they can swipe down and return which is problematic. – Lukas Bimba Sep 25 '19 at 02:26
  • @LukasBimba That seems unrelated; could you ask it as a separate question? That will make it easier to help. Thanks. – matt Sep 25 '19 at 20:44
  • Here is the question: https://stackoverflow.com/questions/58151928/swift-5-ios13-segue-to-another-storyboard-or-unconnected-view-controller-witho – Lukas Bimba Sep 29 '19 at 03:11
  • Hi @matt. I have a scenario like this. I have a window opened full screen and another window as an overlay in a scene. When i click on a button in the full screen and present a view controller (by the accessing the keyWindow using the above answer), it is presented in the overlay window's view controller. Why does the keyWindow not change to the one i interacted with? – Rakesha Shastri Oct 10 '19 at 09:15
  • @RakeshaShastri If you are clicking a button in the full screen you don't _need_ the key window. You don't need _any_ window. You just present on `self` (the view controller that owns the button). – matt Oct 10 '19 at 12:05
  • @matt yes. i am aware of that. But i'm doing a custom transition where i have a view controller inside a view. This view is added as a subview to the full screen view. So i end up needing the window's root view controller. Right now i end up calling makeKey() on the view's window before accessing the keyWindow. – Rakesha Shastri Oct 10 '19 at 12:09
  • @RakeshaShastri Could you ask about this as a separate question? Comments are not the place to iron this out. – matt Oct 10 '19 at 13:02
  • I will as soon as i get time to make a simple, complete, verifiable example. – Rakesha Shastri Oct 10 '19 at 13:08
  • @RakeshaShastri :) Sounds great – matt Oct 10 '19 at 15:50
  • Hi @matt. In a new project with storyboard `UIApplication.shared.windows.filter {$0.isKeyWindow}.first` always returns nil if I try to access it from initial view controller. why is that? – joliejuly Nov 28 '19 at 10:11
  • Why the first window in the array? When I read the documentation for UIWindow's -makeKeyAndVisible and UIApplication's -windows, it seems like we would want the last window. What am I missing? – Mario Feb 03 '20 at 17:00
  • 2
    @Mario It's not the first window in the windows array. It's the first _key_ window in the windows array. – matt Feb 03 '20 at 17:20
  • I thought that the windows array will have windows from all existing scenes, and that each existing scene may have a key window. If that's not the case, what am I misunderstanding? If that is the case, how do we know we want the first and not the last? Thanks! – Mario Feb 03 '20 at 19:25
  • 2
    @Mario But the question presupposes there is only one scene. The problem being solved is merely the deprecation of a certain property. Obviously life is much more complicated if you actually have multiple windows on iPad! If you are really trying to write a multiple window iPad app, good luck to you. – matt Feb 03 '20 at 19:46
  • consider using first(where:) as it is more efficient than your solution (it doesn't create an intermediate array of filtered items) – ramzesenok Feb 22 '20 at 12:57
  • didn't mean to offend you, just noticed that your solution has a lot of upvotes and so many people will use it, though it could be better – ramzesenok Feb 22 '20 at 16:53
  • 2
    @ramzesenok Of course it could be better. But it's not wrong. On the contrary, I was the first to suggest that it might be sufficient to ask the application for a window that is the key window, thus avoiding the deprecation of the `keyWindow` property. Hence the upvotes. If you don't like it, downvote it. But don't tell me to change it to match someone else's answer; that, as I said, would be wrong. – matt Feb 22 '20 at 17:06
  • @Mario said: «I thought [...] that each existing scene may have a key window [...]» The documentation points out: «Only one window at a time may be the key window [for the app, not scene].» As I understand it: Even if you have multiple scenes of your app, the *first* key window in the windows array is also the *only* one. – andreas1724 Jun 03 '20 at 22:58
  • 38
    This now can also be simplified as `UIApplication.shared.windows.first(where: \.isKeyWindow)` – dadalar Jun 04 '20 at 08:20
  • 2
    @dadalar Yes, I really like that syntax (new in Swift 5.2). – matt Jul 21 '20 at 19:11
  • @LukasBimba you can replace your code to this `UIApplication.shared.windows.first?.rootViewController = vc` or simply for this one `self.view.window?.rootViewController = vc` – Carlos Irano Aug 30 '20 at 23:47
  • The accepted answer is actually not working for me in cases where it's called during `applicationWillEnterForeground`. The solution @matt proposes, works. – Martin Sep 09 '20 at 02:24
  • @BenLeggiero Edit privileges should neither be used to replace the core content of an answer by the core content of another answer nor to introduce your own writing style. – pommy Sep 20 '20 at 08:01
  • @pommy The core content remained the same (only performance changed, not behavior nor approach), and [my style](https://swift-style-guidelines.bhstudios.org/Whitespace/#chopping-down-functions) is different. As the help page says, [any time you see a post that needs improvement and are inclined to suggest an edit, you are welcome to do so. \[...\] Edits are expected to be substantial and to leave the post better than you found it.](https://stackoverflow.com/help/editing) For us with edit privilege, [editing is encouraged!](https://stackoverflow.com/help/privileges/edit) – Ky. Sep 23 '20 at 15:51
  • @BenLeggiero OK but turning my answer into pommy's answer is not encouraged. Do not use editing to lie about history. The history is, I gave an answer, pommy gave a different answer, possibly better. That needs to stand. – matt Sep 23 '20 at 16:12
  • I'm sorry I offended you, @matt; that was not my intention. I also didn't intend do lie about history. I was hoping to help folks coming here from Google trying to find a quick answer who might not be interested in reading through all answers to find newer ones. I consider the edit history to be the history of the answer, not related answers alongside this one. – Ky. Sep 23 '20 at 16:18
  • I honestly thought what I was doing was SO best-practices. I've asked a question on Meta to help me understand what happened here: https://meta.stackoverflow.com/q/401472/3939277 – Ky. Sep 23 '20 at 16:58
  • @BenLeggiero I thought so too until I saw that you were making my answer look like pommy's answer. It's my answer so I need to take a stand, and my stand is, this, for better or worse, is what I said. I've upvoted pommy's answer as being a better expression of mine. Your edit makes me seem to "steal" his answer, and I don't wish to do that. When you edit me, you put your words in _my_ mouth, so that needs to be taken into consideration. – matt Sep 23 '20 at 17:08
  • @matt thanks a lot, your simple solution solved my great problem! :) – Ricardo Barroso May 11 '21 at 10:45
  • Even if you *are* supporting multiple windows, you should continue to use it, as there's no good workaround. I have a post that discussed the limitations: https://tengl.net/blog/2021/11/9/uiapplication-key-window-replacement But you are mostly correct. Your answer should be one up top. – Teng L Nov 09 '21 at 17:49
193

This is my solution:

let keyWindow = UIApplication.shared.connectedScenes
        .filter({$0.activationState == .foregroundActive})
        .compactMap({$0 as? UIWindowScene})
        .first?.windows
        .filter({$0.isKeyWindow}).first

Usage e.g.:

keyWindow?.endEditing(true)
Guillaume Algis
  • 10,506
  • 6
  • 41
  • 70
berni
  • 2,353
  • 1
  • 11
  • 9
  • 17
    Thanks - not something that is very intuitive to find out... 8-) – Hardy Jul 23 '19 at 19:08
  • Meanwhile I tested the approach with the multiple scene sample (https://developer.apple.com/documentation/uikit/app_and_environment/scenes/supporting_multiple_windows_on_ipad) and all worked as expected. – berni Jul 24 '19 at 08:27
  • 1
    You just need the get `isKeyWindow`. – NSProgrammer Sep 17 '19 at 16:26
  • 3
    It may also be appropriate to test for the `activationState` value `foregroundInactive` here, which in my testing will be the case if an alert is presented. – Drew Dec 20 '19 at 04:05
  • 1
    @Drew it should be tested because on app start the view controller is already visible but the state is `foregroundInactive` – Gargo Feb 01 '20 at 20:17
  • 5
    This code produces keyWindow = nil for me. `matt` solution is the one that works. – Duck Feb 25 '20 at 15:36
  • 3
    This solution is actually not working for me in cases where it's called during applicationWillEnterForeground. The solution @matt proposes, works. – Martin Sep 09 '20 at 02:25
  • I've updated this answer with a much more performant version of the code, which should give the same result. Let me know if I'm wrong! – Ky. Sep 16 '20 at 21:43
  • `.map({$0 as? UIWindowScene}).compactMap({$0})` can be replaced with `.compactMap { $0 as? UIWindowScene }` – Jordan H Jul 24 '21 at 18:41
  • Nope, this is not the right answer unfortunately—it fails when you have two scenes side by side on the iPad. I have a post here: https://tengl.net/blog/2021/11/9/uiapplication-key-window-replacement – Teng L Nov 09 '21 at 17:48
  • true also somtimes I have nil trying to access this way – Michał Ziobro Mar 02 '22 at 14:44
181

iOS 15, compatible down to iOS 13

UIApplication
.shared
.connectedScenes
.flatMap { ($0 as? UIWindowScene)?.windows ?? [] }
.first { $0.isKeyWindow }

Note that connectedScenes is available only since iOS 13. If you need to support earlier versions of iOS, you have to place this in an if #available(iOS 13, *) statement.

A variant that is longer, but easier to understand:

UIApplication
.shared
.connectedScenes
.compactMap { $0 as? UIWindowScene }
.flatMap { $0.windows }
.first { $0.isKeyWindow }

iOS 13 and 14

The following historical answer is still valid on iOS 15, but should be replaced because UIApplication.shared.windows is deprecated. Thanks to @matt for pointing this out!

Original answer:

Improving slightly on matt's excellent answer, this is even simpler, shorter, and more elegant:

UIApplication.shared.windows.first { $0.isKeyWindow }
pommy
  • 2,347
  • 1
  • 14
  • 20
  • 1
    Thank you! Is there a way to do this in objective c? – Allenktv Sep 21 '19 at 13:21
  • 1
    @Allenktv Unfortunately `NSArray` doesn’t have an equivalent to `first(where:)`. You may try to compose a one-liner with `filteredArrayUsingPredicate:` and `firstObject:`. – pommy Sep 22 '19 at 15:23
  • 1
    @Allenktv the code got mangled in the comments section, so I posted an Objective-C equivalent below. – user2002649 Oct 18 '19 at 09:01
  • Xcode 11.2 compiler reported an error with this answer, and suggested adding parenthesis and it's content to `first(where:)`: `UIApplication.shared.windows.first(where: { $0.isKeyWindow })` – Yassine ElBadaoui Nov 03 '19 at 03:44
  • @YassineElBadaoui The parenthesis is not necessary in a simple assignment 89 – pommy Nov 04 '19 at 08:12
  • 8
    This now can also be simplified as `UIApplication.shared.windows.first(where: \.isKeyWindow)` – dadalar Jun 04 '20 at 08:20
  • @dadalar Your key-path solution is arguably more elegant, but also slightly longer, since you can't drop the the argument label `where` as with the trailing closure. – pommy Jun 05 '20 at 11:52
  • @pommy True, I guess it's a matter of personal taste. Xcode doesn't usually indent trailing closures well, that's why I like the other approach better. – dadalar Jun 05 '20 at 11:58
  • This should have been posted as an edit to Matt's answer – Ky. Sep 17 '20 at 21:31
  • @BenLeggiero Edit privileges should not be used to replace the core content of an answer by the core content of another answer. – pommy Sep 20 '20 at 07:57
  • @pommy The core content is the same. This one has the same concept and approach to a solution, but as you said in the text, it's "simpler, shorter, and more elegant". As the help page says, [any time you see a post that needs improvement and are inclined to suggest an edit, you are welcome to do so. \[...\] Edits are expected to be substantial and to leave the post better than you found it.](https://stackoverflow.com/help/editing) For us with edit privilege, [editing is encouraged!](https://stackoverflow.com/help/privileges/edit) – Ky. Sep 23 '20 at 15:47
  • 1
    Unfortunately `windows` is now deprecated too. – matt Sep 25 '21 at 12:47
  • @matt Thanks, I have updated my answer. I can't avoid creating temporary arrays as in my original answer, but at least there are still fewer than in the accepted answer. I hope the compiler's optimization figures out that it doesn't need to create them. Kotlin's `asSequence()` would come handy here. – pommy Sep 27 '21 at 11:20
  • This method fails on one situation: having two scenes in split screen on the iPad, and both are recognized as "Key Window" in this method. But UIApplication.shared.keyWindow returns one (and it's always the one you last interacted with, indicated by the elevated three-dots on top if you connect to a keyboard) – Teng L Nov 09 '21 at 17:09
  • @TengL you can simply use `filter(\.isKeyWindow)` instead of `first { $0.isKeyWindow }` and return an array with all key windows – Leo Dabus Mar 12 '22 at 14:34
55

Here is a backward-compatible way of detecting keyWindow:

extension UIWindow {
    static var key: UIWindow? {
        if #available(iOS 13, *) {
            return UIApplication.shared.windows.first { $0.isKeyWindow }
        } else {
            return UIApplication.shared.keyWindow
        }
    }
}

Usage:

if let keyWindow = UIWindow.key {
    // Do something
}
Vadim Bulavin
  • 3,449
  • 24
  • 19
  • 7
    This is the most elegant answer and demonstrates how beautiful Swift `extension`s are. – Clifton Labrum Apr 02 '20 at 05:11
  • 2
    The availability checks are hardly necessary, since `windows` and `isKeyWindow` have been around since iOS 2.0, and `first(where:)` since Xcode 9.0 / Swift 4 / 2017. – pommy Jun 05 '20 at 12:02
  • 1
    `UIApplication.keyWindow` has been deprecated on iOS 13.0: _@available(iOS, introduced: 2.0, deprecated: 13.0, message: "Should not be used for applications that support multiple scenes as it returns a key window across all connected scenes")_ – Vadim Bulavin Jun 09 '20 at 07:54
  • @VadimBulavin you did not understand the comment pommy suggested only using`static var key: UIWindow? { UIApplication.shared.windows.first(where: \.isKeyWindow) }` – Leo Dabus Jun 03 '21 at 20:22
40

Usually use

Swift 5

UIApplication.shared.windows.filter {$0.isKeyWindow}.first

In addition,in the UIViewController:

self.view.window

view.window is current window for scenes

WWDC 2019: enter image description here

Key Windows

  • Track windows manually
iHTCboy
  • 2,479
  • 17
  • 18
30

For an Objective-C solution

+(UIWindow*)keyWindow
{
    UIWindow        *foundWindow = nil;
    NSArray         *windows = [[UIApplication sharedApplication]windows];
    for (UIWindow   *window in windows) {
        if (window.isKeyWindow) {
            foundWindow = window;
            break;
        }
    }
    return foundWindow;
}
Cœur
  • 34,719
  • 24
  • 185
  • 251
user2002649
  • 600
  • 4
  • 9
15

A UIApplication extension:

extension UIApplication {

    /// The app's key window taking into consideration apps that support multiple scenes.
    var keyWindowInConnectedScenes: UIWindow? {
        return windows.first(where: { $0.isKeyWindow })
    }

}

Usage:

let myKeyWindow: UIWindow? = UIApplication.shared.keyWindowInConnectedScenes
Daniel Storm
  • 17,279
  • 7
  • 80
  • 145
10

Ideally, since it has been deprecated I would advice you to store the window in the SceneDelegate. However if you do want a temporary workaround, you can create a filter and retrieve the keyWindow just like this.

let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
Chaitanya Ramji
  • 271
  • 3
  • 8
  • This should be a comment or edit to [matt's answer](https://stackoverflow.com/a/57899013/3939277), not a separate answer – Ky. Sep 18 '20 at 16:22
8

If you want to use it in any ViewController then you can simply use.

self.view.window
Simran Singh
  • 367
  • 4
  • 7
7

try with that:

UIApplication.shared.windows.filter { $0.isKeyWindow }.first?.rootViewController!.present(alert, animated: true, completion: nil)
Anas
  • 73
  • 1
  • 3
  • This should be a comment or edit to [matt's answer](https://stackoverflow.com/a/57899013/3939277), not a separate answer – Ky. Sep 18 '20 at 16:23
4

For an Objective-C solution too

@implementation UIWindow (iOS13)

+ (UIWindow*) keyWindow {
   NSPredicate *isKeyWindow = [NSPredicate predicateWithFormat:@"isKeyWindow == YES"];
   return [[[UIApplication sharedApplication] windows] filteredArrayUsingPredicate:isKeyWindow].firstObject;
}

@end
MadWai
  • 41
  • 2
4

As many of developers asking for Objective C code of this deprecation's replacement. You can use this below code to use the keyWindow.

+(UIWindow*)keyWindow {
    UIWindow        *windowRoot = nil;
    NSArray         *windows = [[UIApplication sharedApplication]windows];
    for (UIWindow   *window in windows) {
        if (window.isKeyWindow) {
            windowRoot = window;
            break;
        }
    }
    return windowRoot;
}

I created and added this method in the AppDelegate class as a class method and use it with very simple way that is below.

[AppDelegate keyWindow];

Don't forget to add this method in AppDelegate.h class like below.

+(UIWindow*)keyWindow;
Paras Joshi
  • 20,304
  • 11
  • 55
  • 69
3

Inspired by the answer of berni

let keyWindow = Array(UIApplication.shared.connectedScenes)
        .compactMap { $0 as? UIWindowScene }
        .flatMap { $0.windows }
        .first(where: { $0.isKeyWindow })
Milander
  • 891
  • 10
  • 17
  • I checked this code in a very weird way, and it worked better than the rest. Where others were crashing, it worked regardless of foreground/background... – shanezzar Sep 18 '21 at 09:52
  • Since connectedScenes is a set, is converting to an array necessary here? – Marcy Oct 29 '21 at 00:59
2
NSSet *connectedScenes = [UIApplication sharedApplication].connectedScenes;
for (UIScene *scene in connectedScenes) {
    if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]]) {
        UIWindowScene *windowScene = (UIWindowScene *)scene;
        for (UIWindow *window in windowScene.windows) {
            UIViewController *viewController = window.rootViewController;
            // Get the instance of your view controller
            if ([viewController isKindOfClass:[YOUR_VIEW_CONTROLLER class]]) {
                // Your code here...
                break;
            }
        }
    }
}
Atul Pol
  • 311
  • 3
  • 5
2

Berni's code is nice but it doesn't work when the app comes back from background.

This is my code:

class var safeArea : UIEdgeInsets
{
    if #available(iOS 13, *) {
        var keyWindow = UIApplication.shared.connectedScenes
                .filter({$0.activationState == .foregroundActive})
                .map({$0 as? UIWindowScene})
                .compactMap({$0})
                .first?.windows
                .filter({$0.isKeyWindow}).first
        // <FIX> the above code doesn't work if the app comes back from background!
        if (keyWindow == nil) {
            keyWindow = UIApplication.shared.windows.first { $0.isKeyWindow }
        }
        return keyWindow?.safeAreaInsets ?? UIEdgeInsets()
    }
    else {
        guard let keyWindow = UIApplication.shared.keyWindow else { return UIEdgeInsets() }
        return keyWindow.safeAreaInsets
    }
}
PerfectGamesOnline.com
  • 1,734
  • 1
  • 18
  • 28
  • The part about not working when the app comes back from the background just bit me in production. In the debugger, it always returns a window, but when run manually, it fails on a regular basis when the app is launched from the background – otusweb Sep 22 '21 at 08:29
2

I had answered the question on a duplicate feed, and as I couldn't find an answer here providing as much code (commented), here is my contribution:

(Tested with iOS 15.2 running on Xcode 13.2.1)

extension UIApplication {
    
    var keyWindow: UIWindow? {
        // Get connected scenes
        return UIApplication.shared.connectedScenes
            // Keep only active scenes, onscreen and visible to the user
            .filter { $0.activationState == .foregroundActive }
            // Keep only the first `UIWindowScene`
            .first(where: { $0 is UIWindowScene })
            // Get its associated windows
            .flatMap({ $0 as? UIWindowScene })?.windows
            // Finally, keep only the key window
            .first(where: \.isKeyWindow)
    }
    
}

If you want to find the presented UIViewController in the key UIWindow , here is another extension you could find useful:

extension UIApplication {
    
    var keyWindowPresentedController: UIViewController? {
        var viewController = self.keyWindow?.rootViewController
        
        // If root `UIViewController` is a `UITabBarController`
        if let presentedController = viewController as? UITabBarController {
            // Move to selected `UIViewController`
            viewController = presentedController.selectedViewController
        }
        
        // Go deeper to find the last presented `UIViewController`
        while let presentedController = viewController?.presentedViewController {
            // If root `UIViewController` is a `UITabBarController`
            if let presentedController = presentedController as? UITabBarController {
                // Move to selected `UIViewController`
                viewController = presentedController.selectedViewController
            } else {
                // Otherwise, go deeper
                viewController = presentedController
            }
        }
        
        return viewController
    }
    
}

You can put this wherever you want, but I personally added it as an extension to UIViewController.

This allows me to add more useful extensions, like ones to present UIViewControllers more easily for example:

extension UIViewController {
    
    func presentInKeyWindow(animated: Bool = true, completion: (() -> Void)? = nil) {
        DispatchQueue.main.async {
            UIApplication.shared.keyWindow?.rootViewController?
                .present(self, animated: animated, completion: completion)
        }
    }
    
    func presentInKeyWindowPresentedController(animated: Bool = true, completion: (() -> Void)? = nil) {
        DispatchQueue.main.async {
            UIApplication.shared.keyWindowPresentedController?
                .present(self, animated: animated, completion: completion)
        }
    }
    
}
Rémi B.
  • 1,560
  • 9
  • 17
2

Supports iOS 13 and later.

To keep using similar syntax as the older iOS versions UIApplication.shared.keyWindow create this extension:

extension UIApplication {
    var mainKeyWindow: UIWindow? {
        get {
            if #available(iOS 13, *) {
                return connectedScenes
                    .flatMap { ($0 as? UIWindowScene)?.windows ?? [] }
                    .first { $0.isKeyWindow }
            } else {
                return keyWindow
            }
        }
    }
}

Usage

if let keyWindow = UIApplication.shared.mainKeyWindow {
    // Do Stuff
}
1
- (UIWindow *)mainWindow {
    NSEnumerator *frontToBackWindows = [UIApplication.sharedApplication.windows reverseObjectEnumerator];
    for (UIWindow *window in frontToBackWindows) {
        BOOL windowOnMainScreen = window.screen == UIScreen.mainScreen;
        BOOL windowIsVisible = !window.hidden && window.alpha > 0;
        BOOL windowLevelSupported = (window.windowLevel >= UIWindowLevelNormal);
        BOOL windowKeyWindow = window.isKeyWindow;
        if(windowOnMainScreen && windowIsVisible && windowLevelSupported && windowKeyWindow) {
            return window;
        }
    }
    return nil;
}
Erfan
  • 1,284
  • 1
  • 11
  • 21
1

If your app has not been updated to adopt the Scene based app lifecycle, another simple way to get the active window object is via UIApplicationDelegate:

let window = UIApplication.shared.delegate?.window
let rootViewController = window??.rootViewController
Junfeng
  • 880
  • 8
  • 19
0

I met the same problem. I alloced a newWindow for a view, and set it [newWindow makeKeyAndVisible]; When finished using it, set it [newWindow resignKeyWindow]; and then try to show the original key-window directly by [UIApplication sharedApplication].keyWindow.

Everything is all right on iOS 12, but on iOS 13 the original key-window can't been normal shown. It shows a whole white screen.

I solved this problem by:

UIWindow *mainWindow = nil;
if ( @available(iOS 13.0, *) ) {
   mainWindow = [UIApplication sharedApplication].windows.firstObject;
   [mainWindow makeKeyWindow];
} else {
    mainWindow = [UIApplication sharedApplication].keyWindow;
}

Hope it helps.

coderChrisLee
  • 249
  • 2
  • 6
0

I faced the issue when .foregroundActive scenes were empty

So here is my workaround

public extension UIWindow {
    @objc
    static var main: UIWindow {
        // Here we sort all the scenes in order to work around the case
        // when no .foregroundActive scenes available and we need to look through
        // all connectedScenes in order to find the most suitable one
        let connectedScenes = UIApplication.shared.connectedScenes
            .sorted { lhs, rhs in
                let lhs = lhs.activationState
                let rhs = rhs.activationState
                switch lhs {
                case .foregroundActive:
                    return true
                case .foregroundInactive:
                    return rhs == .background || rhs == .unattached
                case .background:
                    return rhs == .unattached
                case .unattached:
                    return false
                @unknown default:
                    return false
                }
            }
            .compactMap { $0 as? UIWindowScene }

        guard connectedScenes.isEmpty == false else {
            fatalError("Connected scenes is empty")
        }
        let mainWindow = connectedScenes
            .flatMap { $0.windows }
            .first(where: \.isKeyWindow)

        guard let window = mainWindow else {
            fatalError("Couldn't get main window")
        }
        return window
    }
}
Ivan Smetanin
  • 1,949
  • 2
  • 18
  • 28
0

if you're using SwiftLint with 'first_where' rule and wanna to silence warring:

UIApplication.shared.windows.first(where: { $0.isKeyWindow })
polykuzin
  • 11
  • 3
  • `windows` is deprecated in iOS 15. Ref: `@available(iOS, introduced: 2.0, deprecated: 15.0, message: "Use UIWindowScene.windows on a relevant window scene instead")` – Abdurrahman Mubeen Ali Apr 29 '22 at 15:07
0

I've solved with:

let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
let window = windowScene?.windows.first
DungeonDev
  • 1,106
  • 1
  • 12
  • 16