From 7df68829076a9969d0c612727bac3bff909fab77 Mon Sep 17 00:00:00 2001 From: Scott Pfeil Date: Wed, 19 Apr 2023 16:36:16 -0400 Subject: [PATCH] Bug fixes --- MVMCoreUI/Alerts/AlertHandler.swift | 6 +- MVMCoreUI/Alerts/AlertOperation.swift | 67 +++++++++++++++---- MVMCoreUI/Alerts/TopNotificationHandler.swift | 2 +- MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m | 7 +- .../TopAlert/MVMCoreTopAlertViewProtocol.h | 2 +- MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m | 14 ++-- 6 files changed, 74 insertions(+), 24 deletions(-) diff --git a/MVMCoreUI/Alerts/AlertHandler.swift b/MVMCoreUI/Alerts/AlertHandler.swift index abd087e8..0d9f8744 100644 --- a/MVMCoreUI/Alerts/AlertHandler.swift +++ b/MVMCoreUI/Alerts/AlertHandler.swift @@ -16,7 +16,11 @@ public class AlertHandler { } /// The operation queue of alert operations. - private var queue = OperationQueue() + private var queue = { + let queue = OperationQueue() + queue.maxConcurrentOperationCount = 1 + return queue + }() public init() {} diff --git a/MVMCoreUI/Alerts/AlertOperation.swift b/MVMCoreUI/Alerts/AlertOperation.swift index 4f56ca6e..bacc437e 100644 --- a/MVMCoreUI/Alerts/AlertOperation.swift +++ b/MVMCoreUI/Alerts/AlertOperation.swift @@ -24,16 +24,28 @@ public class AlertOperation: MVMCoreOperation { } } private var properties = Properties() - - private var cancellable: Cancellable? - + public let alertController: AlertController + private var alDescription: String = "" + + public let alertObject: AlertObject + /// For tracking isVisible changes of the alert controller. + private var cancellable: Cancellable? + + + /// Blocks the navigation queue to ensure no other navigation happens while an alert is displayed. + private var blockingOperation: MVMCoreOperation? + public init(with alert: AlertController, alertObject: AlertObject) { self.alertController = alert self.alertObject = alertObject + super.init() + MVMCoreDispatchUtility.performSyncBlock(onMainThread: { + self.alDescription = alert.description + }) } deinit { @@ -46,24 +58,38 @@ public class AlertOperation: MVMCoreOperation { // Observe for when it is removed. observeForCurrentAlertViewDismissal() + print("---\nTTTTTT alertOperation present: \(self.description)\ncontroller:\(self.alDescription)\nobject: \(alertObject.alertModel.id)\n---") + // Adds the presentation to the animation queue. - MVMCoreNavigationHandler.shared()?.present(alertController, animated: true, delegate: nil) { [weak self] in - guard let self = self else { return } - Task { - // We finished but it was not displayed yet. It's possible that it was cancelled. Finish this task - if await !self.properties.getIsDisplayed() { - self.markAsFinished() - } else { - (CoreUIObject.sharedInstance()?.loggingDelegate as? MVMCoreUILoggingDelegateProtocol)?.logAlert(with: self.alertObject) - if self.isCancelled { - await self.dismissAlertView() + let blockingOperation = MVMCoreOperation() + self.blockingOperation = blockingOperation + Task { @MainActor in + MVMCoreNavigationHandler.shared()?.present(alertController, animated: true, delegate: nil) { [weak self] in + guard let self = self else { + blockingOperation.markAsFinished() + return + } + Task { + // We finished but it was not displayed yet. It's possible that it was cancelled. Finish this task + if await !self.properties.getIsDisplayed() { + self.markAsFinished() + } else { + (CoreUIObject.sharedInstance()?.loggingDelegate as? MVMCoreUILoggingDelegateProtocol)?.logAlert(with: self.alertObject) + if self.isCancelled { + await self.dismissAlertView() + } } } } + + // Block navigations until this alert is removed. + MVMCoreNavigationHandler.shared()?.addNavigationOperation(blockingOperation) } } - + public override func cancel() { + print("---\nTTTTTT alertOperation cancelled: \(self.description)\ncontroller: \(self.alDescription)\nobject: \(self.alertObject.alertModel.id)\n---") + super.cancel() Task { @MainActor in self.alertObject.alertDelegate?.alertCancelled(self.alertController) @@ -75,13 +101,22 @@ public class AlertOperation: MVMCoreOperation { guard await properties.getIsDisplayed() else { return } await withCheckedContinuation { continuation in Task { @MainActor in + print("---\nTTTTTT alertOperation beginDismiss: \(self.description)\ncontroller: \(self.alDescription)\nobject: \(self.alertObject.alertModel.id)\n---") + MVMCoreNavigationHandler.shared()?.dismiss(alertController, animated: true, delegate: nil) { + print("---\nTTTTTT alertOperation endDismiss: \(self.description)\ncontroller: \(self.alDescription)\nobject: \(self.alertObject.alertModel.id)\n---") + continuation.resume() } } } } + public override func markAsFinished() { + blockingOperation?.markAsFinished() + super.markAsFinished() + } + // MARK: Observer Functions private func observeForCurrentAlertViewDismissal() { @@ -93,8 +128,12 @@ public class AlertOperation: MVMCoreOperation { await self.properties.set(displayed: visible) Task { @MainActor in if visible { + print("---\nTTTTTT alertOperation visible true: \(self.description)\ncontroller:\(self.alDescription)\nobject: \(self.alertObject.alertModel.id)\n---") + self.alertObject.alertDelegate?.alertShown(self.alertController) } else { + print("---\nTTTTTT alertOperation visible false: \(self.description)\ncontroller:\(self.alDescription)\nobject: \(self.alertObject.alertModel.id)\n---") + self.alertObject.alertDelegate?.alertDismissed(self.alertController) // Is visible was set to NO, meaning that the alertview is no longer visible. diff --git a/MVMCoreUI/Alerts/TopNotificationHandler.swift b/MVMCoreUI/Alerts/TopNotificationHandler.swift index 937d174e..25a75c7f 100644 --- a/MVMCoreUI/Alerts/TopNotificationHandler.swift +++ b/MVMCoreUI/Alerts/TopNotificationHandler.swift @@ -148,7 +148,7 @@ public class TopNotificationHandler { guard operation.isExecuting, let operation = operation as? MVMCoreTopAlertOperation else { return false } return operation.topAlertObject.persistent && operation.topAlertObject.type == type - }) as? MVMCoreTopAlertOperation == nil + }) as? MVMCoreTopAlertOperation != nil } /// Shows the top alert with the json. diff --git a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m index 0692de34..b3b6beaa 100644 --- a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m +++ b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m @@ -30,6 +30,9 @@ @property (nonatomic, strong) dispatch_source_t timerSource; +// A reference to the show operation so it can be cancelled. +@property (nonatomic, weak) NSOperation *operation; + @end @implementation MVMCoreTopAlertOperation @@ -177,7 +180,7 @@ } UIView *topAlertView = [[CoreUIObject sharedInstance].globalTopAlertDelegate getTopAlertView]; - [topAlertView showWithTopAlertObject:self.topAlertObject animationDelegate:self completionHandler:^(BOOL finished) { + self.operation = [topAlertView showWithTopAlertObject:self.topAlertObject animationDelegate:self completionHandler:^(BOOL finished) { self.displayed = YES; if (self.isCancelled) { @@ -226,6 +229,8 @@ - (void)cancel { [super cancel]; + [self.operation cancel]; + // Do nothing if animating. if (!self.isAnimating) { diff --git a/MVMCoreUI/TopAlert/MVMCoreTopAlertViewProtocol.h b/MVMCoreUI/TopAlert/MVMCoreTopAlertViewProtocol.h index e799e858..d9b07d0e 100644 --- a/MVMCoreUI/TopAlert/MVMCoreTopAlertViewProtocol.h +++ b/MVMCoreUI/TopAlert/MVMCoreTopAlertViewProtocol.h @@ -14,7 +14,7 @@ @optional /// Show based on the object -- (void)showWithTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id )animationDelegate completionHandler:(void (^ __nullable)(BOOL finished))completionHandler; +- (nonnull NSOperation *)showWithTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id )animationDelegate completionHandler:(void (^ __nullable)(BOOL finished))completionHandler; /// Removes the notification - (void)hideAlertView:(BOOL)forceful completionHandler:(void (^ __nullable)(BOOL finished))completionHandler; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m index 755fc8e9..6ad48095 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m @@ -141,7 +141,7 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; } } -- (void)showAlertView:(nullable UIView *)view topAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject completionHandler:(void (^ __nullable)(BOOL finished))completionHandler { +- (nonnull NSOperation *)showAlertView:(nullable UIView *)view topAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject completionHandler:(void (^ __nullable)(BOOL finished))completionHandler { __weak typeof(self) weakSelf = self; MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) { @@ -176,6 +176,7 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; }]; }]; [[MVMCoreNavigationHandler sharedNavigationHandler] addNavigationOperation:operation]; + return operation; } @@ -190,11 +191,11 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; #pragma mark - MVMCoreTopAlertViewProtocol -- (void)showWithTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id )animationDelegate completionHandler:(void (^ __nullable)(BOOL finished))completionHandler { +- (nonnull NSOperation *)showWithTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id )animationDelegate completionHandler:(void (^ __nullable)(BOOL finished))completionHandler { self.animationDelegate = animationDelegate; - dispatch_async(dispatch_get_main_queue(), ^{ - + __block NSOperation *operation = nil; + [MVMCoreDispatchUtility performSyncBlockOnMainThread:^{ self.topAlertObject = topAlertObject; self.topAlertClearspotView = nil; @@ -208,8 +209,9 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; self.currentAlertOverridingStatusBar = YES; [[MVMCoreUISplitViewController mainSplitViewController] setStatusBarBackgroundColor:statusBarColor style:statusBarStyle]; } - [self showAlertView:view topAlertObject:topAlertObject completionHandler:completionHandler]; - }); + operation = [self showAlertView:view topAlertObject:topAlertObject completionHandler:completionHandler]; + }]; + return operation; } - (void)hideAlertView:(BOOL)forceful completionHandler:(void (^ __nullable)(BOOL finished))completionHandler {