// // 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 = { let queue = OperationQueue() queue.maxConcurrentOperationCount = 1 return queue }() 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: AlertModelProtocol) -> 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.preferredStyle 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) } if let index = alertModel.preferredActionIndex { alertController.preferredAction = alertModel.actions[index] } 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) queue.addOperation(alertOperation) return alertController } /** 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() } }