Aims

  1. Show Alert without view controller
  2. Show Alert on top of screen
  3. The same alert is displayed only once

Ref:

How to present UIAlertController when not in a view controller?

Solution

<aside> 💡 Use an additional UIWindow

</aside>

When you want to display your UIAlertController:

  1. Make your window the key and visible window (window.makeKeyAndVisible())
  2. Just use a plain UIViewController instance as the rootViewController of the new window. (window.rootViewController = UIViewController())
  3. Present your UIAlertController on your window's rootViewController

A couple things to note:


check if alert exists before present alert

// <https://stackoverflow.com/a/58295128/5588637>
class WindowAlertPresentationController: UIViewController {
    
    // MARK: - Properties
    
    private lazy var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
    private let alert: UIAlertController
    
    // MARK: - Initialization
    
    init(alert: UIAlertController) {
        
        self.alert = alert
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder aDecoder: NSCoder) {
        
        fatalError("This initializer is not supported")
    }
    
    // MARK: - Presentation
    
    func present(showOnceOnly: Bool = false, animated: Bool, completion: (() -> Void)?) {
        
        if showOnceOnly {
            var existed = false
            for window in UIApplication.shared.windows {
                if window.rootViewController is WindowAlertPresentationController {
                    existed = true
                    break
                }
            }
            if !existed {
                window?.rootViewController = self
                window?.windowLevel = UIWindow.Level.alert + 1
                window?.makeKeyAndVisible()
                present(alert, animated: animated, completion: completion)
            }
        } else {
            window?.rootViewController = self
            window?.windowLevel = UIWindow.Level.alert + 1
            window?.makeKeyAndVisible()
            present(alert, animated: animated, completion: completion)
        }
        
        
    }
    
    // MARK: - Overrides
    
    override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
        
        super.dismiss(animated: flag) {
            self.window = nil
            completion?()
        }
    }
}
extension UIAlertController {
    func presentInOwnWindow(showOnceOnly: Bool, animated: Bool, completion: (() -> Void)?) {
        let windowAlertPresentationController = WindowAlertPresentationController(alert: self)
        windowAlertPresentationController.present(showOnceOnly: showOnceOnly, animated: animated, completion: completion)
    }
}