// // AlertHandler.swift // MVMCore // // Created by Scott Pfeil on 4/10/23. // Copyright © 2023 myverizon. All rights reserved. // import MVMCore public class AlertHandler { /// Returns the handler stored in the CoreUIObject public static func shared() -> Self { return MVMCoreActionUtility.fatalClassCheck(object: CoreUIObject.sharedInstance()?.alertHandler) } /// The operation queue of alert operations. private var queue = OperationQueue() public init() {} /// Returns if an alert is currently showing in the hierarchy, even if it is not the top presented view. public func isAlertShowing() -> Bool { return queue.operations.contains(where: { operation in return !operation.isCancelled && !operation.isFinished && operation.isExecuting }) } /// Returns if a greedy alert is currently showing in the hierarchy, even if it is not the top presented view. public func isGreedyAlertShowing() -> Bool { return queue.operations.contains(where: { operation in return !operation.isCancelled && !operation.isFinished && operation.isExecuting && (operation as? AlertOperation)?.alertObject.isGreedy ?? false }) } @MainActor public func createAlertController(with alertModel: AlertModel) -> AlertController { // ActionSheets are not supported on iPad interfaces without a source rect (i.e. a source element) which isn't currently supported for our generic handling. // TODO: Find a way to support this. var alertStyle = alertModel.style if alertStyle == .actionSheet, UIDevice.current.userInterfaceIdiom != .phone { alertStyle = .alert } // Create the alert. Adds the actions one by one. let alertController = AlertController(title: alertModel.title, message: alertModel.message, preferredStyle: alertStyle) for action in alertModel.actions { alertController.addAction(action) } return alertController } /// Shows an alert using the alert object. @MainActor public func queueAlertToShow(with alertObject: AlertObject) -> UIAlertController { // It's a greedy alert! Clear all alerts that are queued up and the one that is showing if alertObject.isGreedy { removeAllAlertViews() } let alertController = createAlertController(with: alertObject.alertModel) let alertOperation = AlertOperation(with: alertController, alertObject: alertObject) // If an existing greedy alert is showing, add it as a dependency. if let greedyAlertOperation = queue.operations.first(where: { operation in guard !operation.isFinished, !operation.isCancelled, let alertOperation = operation as? AlertOperation else { return false } return alertOperation.alertObject.isGreedy }) { alertOperation.addDependency((greedyAlertOperation as! AlertOperation)) } queue.addOperation(alertOperation) return alertController } /// Cancel Alert with ID. public func cancelAlert(with id: String) { queue.operations.first { operation in guard let operation = operation as? AlertOperation, operation.alertObject.alertModel.id == id else { return false } return true }?.cancel() } /** Iterates through all scheduled alerts and cancels any that match the provided predicate. * @param predicate The predicate block to decide whether to cancel an alert. */ public func cancelAlert(using predicate: ((AlertObject) -> Bool)) { for case let operation as AlertOperation in queue.operations { if predicate(operation.alertObject) { operation.cancel() } } } /// Cancels all current alerts public func removeAllAlertViews() { queue.cancelAllOperations() } }