23

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!

Rengers
  • 14,293
  • 1
  • 34
  • 53
swiftyboy01
  • 263
  • 1
  • 1
  • 6

1 Answers1

41

(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)
        }
    }
    
}
Rémi B.
  • 1,560
  • 9
  • 17
  • I'm running iOS 15.2 and I added both extensions and the warning still occurs in xCode saying, "'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a relevant window scene instead." What am I doing wrong? – ninu Jan 06 '22 at 19:16
  • Have you cleaned the build folder and rebuilt the project? Are you sure you're not using `.windows` somewhere else? I just tested using XCode 13.2.1 running iOS 15.2 and it builds just fine. – Rémi B. Jan 07 '22 at 20:41
  • For me I had to remove the line `.filter { $0.activationState == .foregroundActive }` to get it to work. A better working solution can be found here: https://stackoverflow.com/a/58031897/12218938 – wristbands May 03 '22 at 22:08
  • You can omit `UIApplication.shared` from the `keyWindow` getter, since `UIApplication` is a singleton, hence, when you access `UIApplication.shared.keyWindow`, you already have access to the `connectedScenes` from within the getter. – haste May 24 '22 at 10:25
  • I don’t understand – Rémi B. May 26 '22 at 08:05