How to resolve: 'keyWindow' was deprecated in iOS 13.0

SwiftIos13UiwindowUiscene

Swift Problem Overview


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?

Swift Solutions


Solution 1 - Swift

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.

Solution 2 - Swift

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)

Solution 3 - Swift

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 }

Solution 4 - Swift

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
}

Solution 5 - Swift

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

Solution 6 - Swift

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;
}

Solution 7 - Swift

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

Solution 8 - Swift

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

Solution 9 - Swift

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

self.view.window

Solution 10 - Swift

try with that:

UIApplication.shared.windows.filter { $0.isKeyWindow }.first?.rootViewController!.present(alert, animated: true, completion: nil)

Solution 11 - Swift

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

Solution 12 - Swift

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;

Solution 13 - Swift

Inspired by the answer of berni

let keyWindow = Array(UIApplication.shared.connectedScenes)
        .compactMap { $0 as? UIWindowScene }
        .flatMap { $0.windows }
        .first(where: { $0.isKeyWindow })

Solution 14 - Swift

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;
            }
        }
    }
}

Solution 15 - Swift

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
    }
}

Solution 16 - Swift

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
}

Solution 17 - Swift

- (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;
}

Solution 18 - Swift

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

Solution 19 - Swift

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)
		}
	}
	
}

Solution 20 - Swift

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.

Solution 21 - Swift

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
    }
}

Solution 22 - Swift

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

UIApplication.shared.windows.first(where: { $0.isKeyWindow })

Solution 23 - Swift

I've solved with:

let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
let window = windowScene?.windows.first

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHardyView Question on Stackoverflow
Solution 1 - SwiftmattView Answer on Stackoverflow
Solution 2 - SwiftberniView Answer on Stackoverflow
Solution 3 - SwiftpommyView Answer on Stackoverflow
Solution 4 - SwiftVadim BulavinView Answer on Stackoverflow
Solution 5 - SwiftiHTCboyView Answer on Stackoverflow
Solution 6 - Swiftuser2002649View Answer on Stackoverflow
Solution 7 - SwiftDaniel StormView Answer on Stackoverflow
Solution 8 - SwiftChaitanya RamjiView Answer on Stackoverflow
Solution 9 - SwiftSimran SinghView Answer on Stackoverflow
Solution 10 - SwiftAnasView Answer on Stackoverflow
Solution 11 - SwiftMadWaiView Answer on Stackoverflow
Solution 12 - SwiftParas JoshiView Answer on Stackoverflow
Solution 13 - SwiftMilanderView Answer on Stackoverflow
Solution 14 - SwiftAtul PolView Answer on Stackoverflow
Solution 15 - SwiftPerfectGamesOnline.comView Answer on Stackoverflow
Solution 16 - SwiftAndi BeqiriView Answer on Stackoverflow
Solution 17 - SwiftErfanView Answer on Stackoverflow
Solution 18 - SwiftJunfengView Answer on Stackoverflow
Solution 19 - SwiftRémi B.View Answer on Stackoverflow
Solution 20 - SwiftcoderChrisLeeView Answer on Stackoverflow
Solution 21 - SwiftIvan SmetaninView Answer on Stackoverflow
Solution 22 - SwiftpolykuzinView Answer on Stackoverflow
Solution 23 - SwiftDungeonDevView Answer on Stackoverflow