Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use UIWindowScene.windows on iOS 15? [duplicate]

Currently, with iOS 14.6, I can call a function in my app that displays a share sheet using the following code:

func share(link: URL) {
    let activityView = UIActivityViewController(activityItems: [link], applicationActivities: nil)
    UIApplication.shared.windows.first?.rootViewController?.present(activityView, animated: true, completion: nil)
}

Since iOS 15 beta, Xcode tells me "'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a relevant window scene instead". How can I update this so that my share sheet will work properly in this new version? Thanks!

like image 665
swiftyboy01 Avatar asked Jul 15 '21 02:07

swiftyboy01


1 Answers

(Tested with iOS 15.2 running on Xcode 13.2.1)

Improving on Rachid's answer, here is a Swiftier version:

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)
        }
    }
    
}
like image 133
Rémi B. Avatar answered Oct 17 '22 21:10

Rémi B.