diff --git a/MVMCoreUI/Alerts/AlertHandler.swift b/MVMCoreUI/Alerts/AlertHandler.swift index e9f1484e..51b7433a 100644 --- a/MVMCoreUI/Alerts/AlertHandler.swift +++ b/MVMCoreUI/Alerts/AlertHandler.swift @@ -44,10 +44,10 @@ public class AlertHandler { } @MainActor - public func createAlertController(with alertModel: AlertModel) -> AlertController { + 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.style + var alertStyle = alertModel.preferredStyle if alertStyle == .actionSheet, UIDevice.current.userInterfaceIdiom != .phone { alertStyle = .alert } @@ -57,6 +57,9 @@ public class AlertHandler { for action in alertModel.actions { alertController.addAction(action) } + if let index = alertModel.preferredActionIndex { + alertController.preferredAction = alertModel.actions[index] + } return alertController } @@ -75,15 +78,6 @@ public class AlertHandler { 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. */ diff --git a/MVMCoreUI/Alerts/AlertObject.swift b/MVMCoreUI/Alerts/AlertObject.swift index fd20a2fa..1cafe734 100644 --- a/MVMCoreUI/Alerts/AlertObject.swift +++ b/MVMCoreUI/Alerts/AlertObject.swift @@ -8,6 +8,14 @@ import MVMCore +public protocol AlertModelProtocol { + var title: String? { get } + var message: String? { get } + var actions: [UIAlertAction] { get } + var preferredActionIndex: Int? { get } + var preferredStyle: UIAlertController.Style { get } +} + /// An object with properties for managing the alert. public struct AlertObject { @@ -15,11 +23,11 @@ public struct AlertObject { public var isGreedy = false /// The alert model for the alert to show. - public var alertModel: AlertModel + public var alertModel: AlertModelProtocol public weak var alertDelegate: AlertDelegateProtocol? - public init(alertModel: AlertModel, isGreedy: Bool = false, alertDelegate: AlertDelegateProtocol? = nil) { + public init(alertModel: AlertModelProtocol, isGreedy: Bool = false, alertDelegate: AlertDelegateProtocol? = nil) { self.alertModel = alertModel self.isGreedy = isGreedy self.alertDelegate = alertDelegate diff --git a/MVMCoreUI/Atomic/Actions/AlertModel.swift b/MVMCoreUI/Atomic/Actions/AlertModel.swift index 96e90054..302c48eb 100644 --- a/MVMCoreUI/Atomic/Actions/AlertModel.swift +++ b/MVMCoreUI/Atomic/Actions/AlertModel.swift @@ -17,6 +17,7 @@ public struct AlertButtonModel: Codable { public var title: String public var action: ActionModelProtocol public var style: UIAlertAction.Style = .default + public var preferred: Bool = false //-------------------------------------------------- // MARK: - Initializer @@ -36,6 +37,7 @@ public struct AlertButtonModel: Codable { case title case action case style + case preferred } //-------------------------------------------------- @@ -50,6 +52,7 @@ public struct AlertButtonModel: Codable { self.style = UIAlertAction.Style(rawValue: style) } action = try typeContainer.decodeModel(codingKey: .action) + preferred = try typeContainer.decodeIfPresent(Bool.self, forKey: .preferred) ?? false } public func encode(to encoder: Encoder) throws { @@ -57,43 +60,50 @@ public struct AlertButtonModel: Codable { try container.encode(title, forKey: .title) try container.encode(style.rawValueString, forKey: .style) try container.encodeModel(action, forKey: .action) + try container.encodeIfPresent(preferred, forKey: .preferred) } } -public struct AlertModel: Codable, Identifiable { +public struct AlertModel: Codable, AlertModelProtocol { + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - - public var title: String - public var message: String - public var style: UIAlertController.Style = .alert - public var actions: [UIAlertAction] - public var buttonModels: [AlertButtonModel]? + + public var title: String? + public var message: String? + public var preferredStyle: UIAlertController.Style = .alert + public var buttonModels: [AlertButtonModel] public var analyticsData: JSONValueDictionary? - public var id: String - - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - public init(title: String, message: String, actions: [UIAlertAction], style: UIAlertController.Style = .alert, id: String = UUID().uuidString) { - self.title = title - self.message = message - self.actions = actions - self.style = style - self.id = id + public var delegateObject: DelegateObject? + + public var actions: [UIAlertAction] { + get { + buttonModels.map({ alertButtonModel in + return alertButtonModel.generateAction(delegateObject: delegateObject) + }) + } } - public init(title: String, message: String, buttonModels: [AlertButtonModel], style: UIAlertController.Style = .alert, delegateObject: DelegateObject?, id: String = UUID().uuidString) { + public var preferredActionIndex: Int? { + get { + buttonModels.firstIndex(where: { alertButtonModel in + return alertButtonModel.preferred + }) + } + } + + + //-------------------------------------------------- + // MARK: - Init + //-------------------------------------------------- + + public init(title: String, message: String, buttonModels: [AlertButtonModel], style: UIAlertController.Style = .alert, delegateObject: DelegateObject?) { self.title = title self.message = message self.buttonModels = buttonModels - actions = buttonModels.map({ alertButtonModel in - return alertButtonModel.generateAction(delegateObject: delegateObject) - }) - self.style = style - self.id = id + self.preferredStyle = style } //-------------------------------------------------- @@ -106,7 +116,6 @@ public struct AlertModel: Codable, Identifiable { case alertActions case style case analyticsData - case id } //-------------------------------------------------- @@ -115,19 +124,15 @@ public struct AlertModel: Codable, Identifiable { public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - let delegateObject = try decoder.get() + delegateObject = try decoder.get() title = try typeContainer.decode(String.self, forKey: .title) message = try typeContainer.decode(String.self, forKey: .message) buttonModels = try typeContainer.decode([AlertButtonModel].self, forKey: .alertActions) - actions = buttonModels!.map({ alertButtonModel in - return alertButtonModel.generateAction(delegateObject: delegateObject) - }) analyticsData = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .analyticsData) if let style = try typeContainer.decodeIfPresent(String.self, forKey: .style) { - self.style = UIAlertController.Style(rawValue: style) + self.preferredStyle = UIAlertController.Style(rawValue: style) } - id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString } public func encode(to encoder: Encoder) throws { @@ -135,9 +140,8 @@ public struct AlertModel: Codable, Identifiable { try container.encode(title, forKey: .title) try container.encode(message, forKey: .message) try container.encodeIfPresent(buttonModels, forKey: .alertActions) - try container.encode(style.rawValueString, forKey: .style) + try container.encode(preferredStyle.rawValueString, forKey: .style) try container.encodeIfPresent(analyticsData, forKey: .analyticsData) - try container.encode(id, forKey: .id) } }