From 271167d50d6c8b94ec377f403260007fbc6fcee8 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 2 Nov 2020 14:41:23 -0500 Subject: [PATCH 1/4] Move to ready logic instead of page dependency --- .../MVMCoreAlertHandler+Extension.swift | 112 ++++++------------ MVMCoreUI/Alerts/MVMCoreAlertHandler.h | 3 - MVMCoreUI/Alerts/MVMCoreAlertHandler.m | 15 +-- MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h | 3 + MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m | 27 +++++ .../MVMCoreUITopAlertView+Extension.swift | 43 +------ 6 files changed, 71 insertions(+), 132 deletions(-) diff --git a/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift b/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift index 309ea8f5..364ee6cb 100644 --- a/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift +++ b/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift @@ -8,88 +8,50 @@ import Foundation -// Temporary, until we can move page checking logic into isReady of topAlertOperation. -@objcMembers public class NotificationPagesOperation: Operation { - public let pages: [String] - - public init(with pages: [String]) { - self.pages = pages - } - - public override var isReady: Bool { - get { - return super.isReady && isCancelled - } - } -} - -// TODO: Move this type of logic into isReady for the top alert operation... then can remove this dependency operation. public extension MVMCoreAlertHandler { - /// Adds a page type dependency to the operation - @objc func addPagesDependency(to operation: MVMCoreTopAlertOperation) { - // This notification may only show for certain pages. - guard let pages = operation.topAlertObject.json?.optionalArrayForKey("pages") as? [String], - pages.count > 0 else { return } - let pagesOperation = NotificationPagesOperation(with: pages) - pageOperations.addOperation(pagesOperation) - operation.addDependency(pagesOperation) - } - - /// Updates the pages requirement for the operation - @objc func updatePages(for operation: MVMCoreTopAlertOperation, with topAlertObject: MVMCoreTopAlertObject) { - // Add new dependencies, remove old. - let previousPages = operation.dependencies.filter { $0 as? NotificationPagesOperation != nil } - addPagesDependency(to: operation) - for pageOperation in previousPages { - pageOperation.cancel() - operation.removeDependency(pageOperation) - } - } - - /// Handles page dependencies for all top alerts with the new pageType - @objc func handleAllPagesDependency(for pageType: String?) { - var currentOperation: MVMCoreTopAlertOperation? = nil - for case let operation as MVMCoreTopAlertOperation in topAlertQueue.operations { - // Handle the currently executing opration last to avoid race conditions. - guard !operation.isExecuting else { - currentOperation = operation - continue - } - handlePageDependency(for: operation, with: pageType) - } - if let operation = currentOperation { - handlePageDependency(for: operation, with: pageType) - } - } - - /// Updates the operation based on the page type and its dependencies. - @objc func handlePageDependency(for operation: MVMCoreTopAlertOperation, with pageType: String?) { - guard !operation.isCancelled else { return } - - if let pages = operation.topAlertObject.json?.optionalArrayForKey("pages") as? [String], - pages.count > 0, - (pageType == nil || !pages.contains(pageType!)) { - if let dependency = operation.dependencies.first(where: { $0.isFinished && ($0 as? NotificationPagesOperation)?.pages == pages }) { - // Re-add the dependency if it was previously cancelled. - let pagesOperation = NotificationPagesOperation(with: pages) - pageOperations.addOperation(pagesOperation) - operation.addDependency(pagesOperation) - operation.removeDependency(dependency) + /// Re-evaluates the queue operations + @objc func reevaluteQueue() { + var highestReadyOperation: MVMCoreTopAlertOperation? + var executingOperation: MVMCoreTopAlertOperation? + let sortedOperations = topAlertQueue.operations.sorted { $0.queuePriority.rawValue > $1.queuePriority.rawValue } + for case let operation as MVMCoreTopAlertOperation in sortedOperations { + guard !operation.isCancelled, + !operation.isFinished else { continue } + operation.refreshReady() + if operation.isReady, + highestReadyOperation == nil { + highestReadyOperation = operation } if operation.isExecuting { - // If the current running operation should not show on the current page, cancel it. If it's persistent, re-add it - operation.reAddAfterCancel = operation.topAlertObject.persistent - operation.cancel() + executingOperation = operation } } + guard let currentOperation = executingOperation else { return } - // Cancel any dependency if it contains the current page. - guard let pageType = pageType else { return } - for case let operation as NotificationPagesOperation in operation.dependencies { - if operation.pages.contains(pageType) { - operation.cancel() - } + // Cancel the executing operation if it is no longer ready to run. Re-add for later if it is persistent. + guard currentOperation.isReady else { + currentOperation.reAddAfterCancel = currentOperation.topAlertObject.persistent + currentOperation.cancel() + return + } + + // If the highest priority operation is not executing, and the executing operation is persistent, cancel it. + if let newOperation = highestReadyOperation, + currentOperation != newOperation, + currentOperation.topAlertObject.persistent { + currentOperation.reAddAfterCancel = true + currentOperation.cancel() } } + + /// Registers with the notification center to know when pages change. + @objc func registerWithNotificationCenter() { + NotificationCenter.default.addObserver(self, selector: #selector(viewControllerChanged(notification:)), name: NSNotification.Name(rawValue: MVMCoreNotificationViewControllerChanged), object: nil) + } + + /// When a detail page changes, check top alerts. + @objc private func viewControllerChanged(notification: Notification) { + reevaluteQueue() + } } diff --git a/MVMCoreUI/Alerts/MVMCoreAlertHandler.h b/MVMCoreUI/Alerts/MVMCoreAlertHandler.h index f3b30efd..522c5740 100644 --- a/MVMCoreUI/Alerts/MVMCoreAlertHandler.h +++ b/MVMCoreUI/Alerts/MVMCoreAlertHandler.h @@ -23,9 +23,6 @@ // An operation queue for top alerts @property (nonnull, strong, nonatomic) NSOperationQueue *topAlertQueue; -/// Stores any page dependencies for top alerts -@property (nonnull, strong, nonatomic) NSOperationQueue *pageOperations; - /// Returns the shared instance of this singleton + (nullable instancetype)sharedAlertHandler; diff --git a/MVMCoreUI/Alerts/MVMCoreAlertHandler.m b/MVMCoreUI/Alerts/MVMCoreAlertHandler.m index 4e7be1f7..f65a1db0 100644 --- a/MVMCoreUI/Alerts/MVMCoreAlertHandler.m +++ b/MVMCoreUI/Alerts/MVMCoreAlertHandler.m @@ -14,7 +14,7 @@ #import #import #import -#import +#import @interface MVMCoreAlertHandler () @@ -42,7 +42,7 @@ self.popupAlertQueue.maxConcurrentOperationCount = 1; self.topAlertQueue = [[NSOperationQueue alloc] init]; self.topAlertQueue.maxConcurrentOperationCount = 1; - self.pageOperations = [[NSOperationQueue alloc] init]; + [self registerWithNotificationCenter]; } return self; } @@ -173,16 +173,7 @@ }]; [self.topAlertQueue addOperation:alertOperation]; - - // If the current running operation is persistent and has a lower queue priority then the added operation, cancel it and re-add it. - for (MVMCoreTopAlertOperation *operation in self.topAlertQueue.operations) { - - if (operation.isExecuting && !operation.isCancelled && operation.topAlertObject.persistent && operation.queuePriority < alertOperation.queuePriority && alertOperation.isReady) { - operation.reAddAfterCancel = YES; - [operation cancel]; - break; - } - } + [self reevaluteQueue]; } - (void)showTopAlertWithObject:(nullable MVMCoreTopAlertObject *)topAlertObject { diff --git a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h index 73acec1e..86831e89 100644 --- a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h +++ b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h @@ -30,4 +30,7 @@ // Unpauses the operation, resuming any alert. - (void)unpause; +// NSOperationQueue uses KVO, this forces it to aknowledge changes +- (void)refreshReady; + @end diff --git a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m index bcf9515a..f721734a 100644 --- a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m +++ b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m @@ -10,6 +10,7 @@ #import "MVMCoreTopAlertObject.h" #import "MVMCoreAlertHandler.h" #import +#import @interface MVMCoreTopAlertOperation () { __block BOOL _paused; @@ -108,6 +109,27 @@ }); } +// Ensure the current page is supported for this top alert. +- (BOOL)isReady { + BOOL isReady = [super isReady]; + if (self.isCancelled || self.isFinished) { + return isReady; + } + + NSArray *pages = [self.topAlertObject.json array:@"pages"]; + if (pages.count == 0) { + return isReady; + } + + UIViewController *detailViewController = [[MVMCoreUISplitViewController mainSplitViewController] getCurrentDetailViewController]; + if (![detailViewController conformsToProtocol:@protocol(MVMCoreViewControllerProtocol)]) { + return isReady; + } + + NSString *pageType = ((UIViewController *)detailViewController).pageType; + return isReady && [pages containsObject:pageType]; +} + - (void)main { // Always check for cancellation before launching the task. @@ -232,6 +254,11 @@ } } +- (void)refreshReady { + [self willChangeValueForKey:@"isReady"]; + [self didChangeValueForKey:@"isReady"]; +} + #pragma mark - Delegate functions - (void)topAlertViewBeginAnimation { diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift index 9bceb57a..cb53b18a 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift @@ -18,7 +18,6 @@ public extension MVMCoreUITopAlertView { /// Registers with the notification center to know when json is updated. @objc func registerWithNotificationCenter() { NotificationCenter.default.addObserver(self, selector: #selector(responseJSONUpdated(notification:)), name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(viewControllerChanged(notification:)), name: NSNotification.Name(rawValue: MVMCoreNotificationViewControllerChanged), object: nil) } private func getDelegateObject() -> MVMCoreUIDelegateObject { @@ -42,42 +41,6 @@ public extension MVMCoreUITopAlertView { showTopAlert(with: model) } - /// When a detail page changes, check top alerts. - @objc private func viewControllerChanged(notification: Notification) { - guard let controller = MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() as? MVMCoreViewControllerProtocol else { return } - MVMCoreAlertHandler.shared()?.handleAllPagesDependency(for: controller.pageType) - reevalutePriority() - } - - /// Re-evaluates the queue priority - private func reevalutePriority() { - guard let operations = MVMCoreAlertHandler.shared()?.topAlertQueue.operations else { return } - var highestReadyOperation: Operation? - var executingOperation: Operation? - for operation in operations { - guard !operation.isCancelled, - !operation.isFinished else { - continue - } - if operation.isReady, - highestReadyOperation == nil || operation.queuePriority.rawValue > highestReadyOperation!.queuePriority.rawValue { - highestReadyOperation = operation - } - if operation.isExecuting { - executingOperation = operation - } - } - - // If the highest priority operation is not executing, and the executing operation is persistent, cancel it. - if let newOperation = highestReadyOperation, - let currentOperation = executingOperation as? MVMCoreTopAlertOperation, - currentOperation != newOperation, - currentOperation.topAlertObject.persistent { - currentOperation.reAddAfterCancel = true - currentOperation.cancel() - } - } - /// Decodes the json into a TopNotificationModel private func decodeTopNotification(with json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) -> TopNotificationModel? { do { @@ -95,8 +58,6 @@ public extension MVMCoreUITopAlertView { let object = model.createTopAlertObject() guard !checkAndUpdateExisting(with: object), let operation = MVMCoreTopAlertOperation(topAlertObject: object) else { return } - MVMCoreAlertHandler.shared()?.addPagesDependency(to: operation) - MVMCoreAlertHandler.shared()?.handlePageDependency(for: operation, with: (MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() as? MVMCoreViewControllerProtocol)?.pageType) MVMCoreAlertHandler.shared()?.add(operation) } @@ -107,9 +68,7 @@ public extension MVMCoreUITopAlertView { guard topAlertObject.json != nil, operation.topAlertObject.type == topAlertObject.type else { continue } operation.update(with: topAlertObject) - MVMCoreAlertHandler.shared()?.updatePages(for: operation, with: topAlertObject) - MVMCoreAlertHandler.shared()?.handlePageDependency(for: operation, with: (MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() as? MVMCoreViewControllerProtocol)?.pageType) - reevalutePriority() + MVMCoreAlertHandler.shared()?.reevaluteQueue() return true } return false From 987c178c35739a6eed0ed372e1fbbd2419879bb6 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 2 Nov 2020 19:03:23 -0500 Subject: [PATCH 2/4] Review changes for kvo cleanliness --- .../MVMCoreAlertHandler+Extension.swift | 29 +++++++--- MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h | 6 +-- MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m | 54 +++++++++++-------- 3 files changed, 56 insertions(+), 33 deletions(-) diff --git a/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift b/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift index 364ee6cb..a2a8f665 100644 --- a/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift +++ b/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift @@ -12,19 +12,19 @@ public extension MVMCoreAlertHandler { /// Re-evaluates the queue operations @objc func reevaluteQueue() { - var highestReadyOperation: MVMCoreTopAlertOperation? + var higherPriorityOperationIsReady = false var executingOperation: MVMCoreTopAlertOperation? + let pageType = (MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() as? MVMCoreViewControllerProtocol)?.pageType let sortedOperations = topAlertQueue.operations.sorted { $0.queuePriority.rawValue > $1.queuePriority.rawValue } for case let operation as MVMCoreTopAlertOperation in sortedOperations { guard !operation.isCancelled, !operation.isFinished else { continue } - operation.refreshReady() - if operation.isReady, - highestReadyOperation == nil { - highestReadyOperation = operation - } + updatePageReadiness(for: operation, with: pageType) if operation.isExecuting { executingOperation = operation + } else if operation.isReady, + executingOperation == nil { + higherPriorityOperationIsReady = true } } guard let currentOperation = executingOperation else { return } @@ -37,8 +37,7 @@ public extension MVMCoreAlertHandler { } // If the highest priority operation is not executing, and the executing operation is persistent, cancel it. - if let newOperation = highestReadyOperation, - currentOperation != newOperation, + if higherPriorityOperationIsReady, currentOperation.topAlertObject.persistent { currentOperation.reAddAfterCancel = true currentOperation.cancel() @@ -50,6 +49,20 @@ public extension MVMCoreAlertHandler { NotificationCenter.default.addObserver(self, selector: #selector(viewControllerChanged(notification:)), name: NSNotification.Name(rawValue: MVMCoreNotificationViewControllerChanged), object: nil) } + /// Updates the operation isDisplayable based on the page type. + private func updatePageReadiness(for topOperation: MVMCoreTopAlertOperation, with pageType: String?) { + guard let pages = topOperation.topAlertObject.json?.optionalArrayForKey("pages") as? [String], + pages.count != 0 else { + topOperation.isDisplayable = true + return + } + guard let pageType = pageType else { + topOperation.isDisplayable = false + return + } + topOperation.isDisplayable = pages.contains(pageType) + } + /// When a detail page changes, check top alerts. @objc private func viewControllerChanged(notification: Notification) { reevaluteQueue() diff --git a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h index 86831e89..16f2ab8d 100644 --- a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h +++ b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h @@ -15,6 +15,9 @@ @property (readonly, getter=isPaused) BOOL paused; +/// A bool for if this top alert can be displayed. It is only ready if true. +@property (nonatomic, getter=isDisplayable) BOOL displayable; + @property (nonatomic) BOOL reAddAfterCancel; @property (nonnull, nonatomic, strong) MVMCoreTopAlertObject *topAlertObject; @@ -30,7 +33,4 @@ // Unpauses the operation, resuming any alert. - (void)unpause; -// NSOperationQueue uses KVO, this forces it to aknowledge changes -- (void)refreshReady; - @end diff --git a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m index f721734a..a59248ab 100644 --- a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m +++ b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m @@ -16,6 +16,7 @@ __block BOOL _paused; __block BOOL _displayed; __block BOOL _animating; + __block BOOL _displayable; } @property (readwrite, getter=isPaused) BOOL paused; @@ -26,6 +27,7 @@ @property (strong, nonatomic) dispatch_queue_t pausedQueue; @property (strong, nonatomic) dispatch_queue_t displayedQueue; @property (strong, nonatomic) dispatch_queue_t animatingQueue; +@property (strong, nonatomic) dispatch_queue_t displayableQueue; @property (nonatomic, strong) dispatch_source_t timerSource; @@ -40,6 +42,8 @@ self.pausedQueue = dispatch_queue_create("paused", DISPATCH_QUEUE_CONCURRENT); self.displayedQueue = dispatch_queue_create("displayed", DISPATCH_QUEUE_CONCURRENT); self.animatingQueue = dispatch_queue_create("animating", DISPATCH_QUEUE_CONCURRENT); + self.displayableQueue = dispatch_queue_create("displayable", DISPATCH_QUEUE_CONCURRENT); + self.displayable = YES; } return self; } @@ -109,25 +113,35 @@ }); } -// Ensure the current page is supported for this top alert. + +- (BOOL)isDisplayable { + __block BOOL isDisplayable; + dispatch_sync(self.displayableQueue, ^{ + isDisplayable = self->_displayable; + }); + return isDisplayable; +} + +- (void)setDisplayable:(BOOL)displayable { + if (displayable != self.isDisplayable) { + BOOL isReady = [super isReady]; + if (isReady) { + [self willChangeValueForKey:@"isReady"]; + } + dispatch_barrier_async(self.displayableQueue, ^{ + self->_displayable = displayable; + }); + if (isReady) { + [self didChangeValueForKey:@"isReady"]; + } + } +} + - (BOOL)isReady { - BOOL isReady = [super isReady]; - if (self.isCancelled || self.isFinished) { - return isReady; + if (self.isCancelled) { + return [super isReady]; } - - NSArray *pages = [self.topAlertObject.json array:@"pages"]; - if (pages.count == 0) { - return isReady; - } - - UIViewController *detailViewController = [[MVMCoreUISplitViewController mainSplitViewController] getCurrentDetailViewController]; - if (![detailViewController conformsToProtocol:@protocol(MVMCoreViewControllerProtocol)]) { - return isReady; - } - - NSString *pageType = ((UIViewController *)detailViewController).pageType; - return isReady && [pages containsObject:pageType]; + return [super isReady] && self.isDisplayable; } - (void)main { @@ -254,11 +268,6 @@ } } -- (void)refreshReady { - [self willChangeValueForKey:@"isReady"]; - [self didChangeValueForKey:@"isReady"]; -} - #pragma mark - Delegate functions - (void)topAlertViewBeginAnimation { @@ -278,6 +287,7 @@ copyObject.topAlertObject = self.topAlertObject; copyObject.paused = self.paused; copyObject.reAddAfterCancel = self.reAddAfterCancel; + copyObject.displayable = self.isDisplayable; copyObject.queuePriority = self.queuePriority; for (NSOperation *dependency in self.dependencies) { [copyObject addDependency:dependency]; From 4a47ec13c1bbe142919d9782fea0661c72cdd900 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 3 Nov 2020 15:55:43 -0500 Subject: [PATCH 3/4] Review Changes --- .../MVMCoreAlertHandler+Extension.swift | 52 ++++++++----------- MVMCoreUI/Alerts/MVMCoreAlertHandler.m | 5 +- MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h | 3 ++ MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m | 13 +++++ .../MVMCoreUITopAlertView+Extension.swift | 2 + 5 files changed, 45 insertions(+), 30 deletions(-) diff --git a/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift b/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift index a2a8f665..760e92c9 100644 --- a/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift +++ b/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift @@ -9,22 +9,20 @@ import Foundation public extension MVMCoreAlertHandler { - + /// Re-evaluates the queue operations @objc func reevaluteQueue() { - var higherPriorityOperationIsReady = false + var highestReadyOperation: MVMCoreTopAlertOperation? var executingOperation: MVMCoreTopAlertOperation? - let pageType = (MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() as? MVMCoreViewControllerProtocol)?.pageType - let sortedOperations = topAlertQueue.operations.sorted { $0.queuePriority.rawValue > $1.queuePriority.rawValue } - for case let operation as MVMCoreTopAlertOperation in sortedOperations { + for case let operation as MVMCoreTopAlertOperation in topAlertQueue.operations { guard !operation.isCancelled, !operation.isFinished else { continue } - updatePageReadiness(for: operation, with: pageType) + if operation.isReady, + highestReadyOperation == nil || operation.queuePriority.rawValue > highestReadyOperation!.queuePriority.rawValue { + highestReadyOperation = operation + } if operation.isExecuting { executingOperation = operation - } else if operation.isReady, - executingOperation == nil { - higherPriorityOperationIsReady = true } } guard let currentOperation = executingOperation else { return } @@ -37,34 +35,30 @@ public extension MVMCoreAlertHandler { } // If the highest priority operation is not executing, and the executing operation is persistent, cancel it. - if higherPriorityOperationIsReady, + if let newOperation = highestReadyOperation, + currentOperation != newOperation, currentOperation.topAlertObject.persistent { currentOperation.reAddAfterCancel = true currentOperation.cancel() } } - /// Registers with the notification center to know when pages change. - @objc func registerWithNotificationCenter() { - NotificationCenter.default.addObserver(self, selector: #selector(viewControllerChanged(notification:)), name: NSNotification.Name(rawValue: MVMCoreNotificationViewControllerChanged), object: nil) + /// Registers to know when pages change. + @objc func registerForPageChanges() { + MVMCoreNavigationHandler.shared()?.addDelegate(self) } - - /// Updates the operation isDisplayable based on the page type. - private func updatePageReadiness(for topOperation: MVMCoreTopAlertOperation, with pageType: String?) { - guard let pages = topOperation.topAlertObject.json?.optionalArrayForKey("pages") as? [String], - pages.count != 0 else { - topOperation.isDisplayable = true - return +} + +extension MVMCoreAlertHandler: MVMCorePresentationDelegateProtocol { + // Update displayable for each top alert operation when page type changes, in top queue priority order. + public func navigationController(_ navigationController: UINavigationController, displayedViewController viewController: UIViewController) { + guard navigationController == MVMCoreUISplitViewController.main()?.navigationController else { return } + let pageType = (viewController as? MVMCoreViewControllerProtocol)?.pageType + let sortedOperations = topAlertQueue.operations.sorted { $0.queuePriority.rawValue > $1.queuePriority.rawValue } + for case let operation as MVMCoreTopAlertOperation in sortedOperations { + operation.updateDisplayable(byPageType: pageType) } - guard let pageType = pageType else { - topOperation.isDisplayable = false - return - } - topOperation.isDisplayable = pages.contains(pageType) - } - - /// When a detail page changes, check top alerts. - @objc private func viewControllerChanged(notification: Notification) { reevaluteQueue() } } + diff --git a/MVMCoreUI/Alerts/MVMCoreAlertHandler.m b/MVMCoreUI/Alerts/MVMCoreAlertHandler.m index f65a1db0..8b4b02c7 100644 --- a/MVMCoreUI/Alerts/MVMCoreAlertHandler.m +++ b/MVMCoreUI/Alerts/MVMCoreAlertHandler.m @@ -42,7 +42,7 @@ self.popupAlertQueue.maxConcurrentOperationCount = 1; self.topAlertQueue = [[NSOperationQueue alloc] init]; self.topAlertQueue.maxConcurrentOperationCount = 1; - [self registerWithNotificationCenter]; + [self registerForPageChanges]; } return self; } @@ -172,6 +172,9 @@ alertOperation = nil; }]; + NSString *currentPageType = ((UIViewController *)[[MVMCoreUISplitViewController mainSplitViewController] getCurrentDetailViewController]).pageType; + [alertOperation updateDisplayableByPageType:currentPageType]; + [self.topAlertQueue addOperation:alertOperation]; [self reevaluteQueue]; } diff --git a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h index 16f2ab8d..af00d789 100644 --- a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h +++ b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.h @@ -27,6 +27,9 @@ /// Updates the operation with a new object - (void)updateWithTopAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject; +/// Updates the operation isDisplayable based on the page type. +- (void)updateDisplayableByPageType:(nullable NSString *)pageType; + // Pauses the operation. Temporarily removes any alert. - (void)pause; diff --git a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m index a59248ab..7a57f989 100644 --- a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m +++ b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m @@ -71,6 +71,19 @@ } } +- (void)updateDisplayableByPageType:(nullable NSString *)pageType { + NSArray *pages = [self.topAlertObject.json array:@"pages"]; + if (pages.count == 0) { + self.displayable = YES; + return; + } + if (pageType.length == 0) { + self.displayable = NO; + return; + } + self.displayable = [pages containsObject:pageType]; +} + - (BOOL)isPaused { __block BOOL isPaused; dispatch_sync(self.pausedQueue, ^{ diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift index cb53b18a..a0b128b2 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift @@ -68,6 +68,8 @@ public extension MVMCoreUITopAlertView { guard topAlertObject.json != nil, operation.topAlertObject.type == topAlertObject.type else { continue } operation.update(with: topAlertObject) + let pageType = (MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() as? MVMCoreViewControllerProtocol)?.pageType + operation.updateDisplayable(byPageType: pageType) MVMCoreAlertHandler.shared()?.reevaluteQueue() return true } From 6ce04350ad290d5ef88904bbbfc60aadd58437bc Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 3 Nov 2020 17:24:41 -0500 Subject: [PATCH 4/4] update for review --- MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift b/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift index 760e92c9..b0158e2d 100644 --- a/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift +++ b/MVMCoreUI/Alerts/MVMCoreAlertHandler+Extension.swift @@ -54,9 +54,12 @@ extension MVMCoreAlertHandler: MVMCorePresentationDelegateProtocol { public func navigationController(_ navigationController: UINavigationController, displayedViewController viewController: UIViewController) { guard navigationController == MVMCoreUISplitViewController.main()?.navigationController else { return } let pageType = (viewController as? MVMCoreViewControllerProtocol)?.pageType - let sortedOperations = topAlertQueue.operations.sorted { $0.queuePriority.rawValue > $1.queuePriority.rawValue } - for case let operation as MVMCoreTopAlertOperation in sortedOperations { - operation.updateDisplayable(byPageType: pageType) + topAlertQueue.operations.compactMap { + $0 as? MVMCoreTopAlertOperation + }.sorted { + $0.queuePriority.rawValue > $1.queuePriority.rawValue + }.forEach { + $0.updateDisplayable(byPageType: pageType) } reevaluteQueue() }