From 034133e4a5f543907374c90508e81a5e3fd0adef Mon Sep 17 00:00:00 2001 From: Scott Pfeil Date: Mon, 28 Aug 2023 11:13:39 -0400 Subject: [PATCH] ONEAPP-5208: Review feedback --- MVMCore/MVMCore.xcodeproj/project.pbxproj | 4 -- ...VMCoreLoadRequestOperation+Extension.swift | 6 +- .../NavigationHandler.swift | 17 ++--- .../NavigationOperation.swift | 67 +++++++++++++++++-- .../Utility/Helpers/KVOPublisher.swift | 65 ------------------ 5 files changed, 74 insertions(+), 85 deletions(-) delete mode 100644 MVMCore/MVMCore/Utility/Helpers/KVOPublisher.swift diff --git a/MVMCore/MVMCore.xcodeproj/project.pbxproj b/MVMCore/MVMCore.xcodeproj/project.pbxproj index bc589d2..5b4c145 100644 --- a/MVMCore/MVMCore.xcodeproj/project.pbxproj +++ b/MVMCore/MVMCore.xcodeproj/project.pbxproj @@ -96,7 +96,6 @@ AF60A7F2289212CA00919EEB /* MVMError.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F1289212CA00919EEB /* MVMError.swift */; }; AF60A7F4289212EB00919EEB /* MVMCoreError.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F3289212EB00919EEB /* MVMCoreError.swift */; }; AF686FDA2A8A876A008F666A /* NavigationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF686FD92A8A876A008F666A /* NavigationOperation.swift */; }; - AF686FDC2A8A87EA008F666A /* KVOPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF686FDB2A8A87EA008F666A /* KVOPublisher.swift */; }; AF686FDE2A8D29CD008F666A /* MVMCoreLoadRequestOperation+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF686FDD2A8D29CD008F666A /* MVMCoreLoadRequestOperation+Extension.swift */; }; AF69D4E9286E54D500BC6862 /* ActionCallHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF69D4E8286E54D500BC6862 /* ActionCallHandler.swift */; }; AF69D4EB286E586200BC6862 /* ActionRestartHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF69D4EA286E586200BC6862 /* ActionRestartHandler.swift */; }; @@ -252,7 +251,6 @@ AF60A7F1289212CA00919EEB /* MVMError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMError.swift; sourceTree = ""; }; AF60A7F3289212EB00919EEB /* MVMCoreError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreError.swift; sourceTree = ""; }; AF686FD92A8A876A008F666A /* NavigationOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationOperation.swift; sourceTree = ""; }; - AF686FDB2A8A87EA008F666A /* KVOPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KVOPublisher.swift; sourceTree = ""; }; AF686FDD2A8D29CD008F666A /* MVMCoreLoadRequestOperation+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreLoadRequestOperation+Extension.swift"; sourceTree = ""; }; AF69D4E8286E54D500BC6862 /* ActionCallHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCallHandler.swift; sourceTree = ""; }; AF69D4EA286E586200BC6862 /* ActionRestartHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionRestartHandler.swift; sourceTree = ""; }; @@ -595,7 +593,6 @@ AF43A5851FBB67D6008E9347 /* MVMCoreActionUtility.h */, AF43A5861FBB67D6008E9347 /* MVMCoreActionUtility.m */, AFA4931D29E5C988001A9663 /* MVMCoreActionUtility+Extension.swift */, - AF686FDB2A8A87EA008F666A /* KVOPublisher.swift */, ); path = Helpers; sourceTree = ""; @@ -940,7 +937,6 @@ D2DEDCBB23C65BC300C44CC4 /* Percent.swift in Sources */, AF686FDE2A8D29CD008F666A /* MVMCoreLoadRequestOperation+Extension.swift in Sources */, 60CBD0542A02397A00056CB0 /* MVMCoreSessionTimeHandler.swift in Sources */, - AF686FDC2A8A87EA008F666A /* KVOPublisher.swift in Sources */, AFBB966A1FBA3A570008D868 /* MVMCoreLoadRequestOperation.m in Sources */, D268D82C26700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.m in Sources */, 0AEBB84625FA75C000EA80EE /* ActionOpenSMSModel.swift in Sources */, diff --git a/MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation+Extension.swift b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation+Extension.swift index 2402b7d..181dae8 100644 --- a/MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation+Extension.swift +++ b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation+Extension.swift @@ -70,13 +70,15 @@ public extension MVMCoreLoadRequestOperation { func navigate(with navigationOperation: NavigationOperation, loadObject: MVMCoreLoadObject?) async { // stop any loading animation we may have started if we are about to display - cancellable = NavigationHandler.shared().onNavigationWillBegin.sink(receiveValue: { operation in + cancellable = NavigationHandler.shared().onNavigation + .filter { $0.0 == .willNavigate } + .sink { (event, operation) in if navigationOperation == operation, !self.backgroundLoad, !(loadObject?.requestParameters?.noloadingOverlay ?? false) { MVMCoreLoadingOverlayHandler.sharedLoadingOverlay()?.stopLoading(false) } - }) + } await NavigationHandler.shared().navigate(with: navigationOperation) cancellable = nil } diff --git a/MVMCore/MVMCore/PresentationHandling/NavigationHandler.swift b/MVMCore/MVMCore/PresentationHandling/NavigationHandler.swift index 7a18051..2dfde17 100644 --- a/MVMCore/MVMCore/PresentationHandling/NavigationHandler.swift +++ b/MVMCore/MVMCore/PresentationHandling/NavigationHandler.swift @@ -42,6 +42,11 @@ public class NavigationHandler { } } } + + public enum NavigationEvent { + case willNavigate + case didNavigate + } private static let handler = NavigationHandler() public static func shared() -> NavigationHandler { @@ -65,15 +70,11 @@ public class NavigationHandler { // MARK: - Publishers - /** Publishes when the navigation will begin - - Parameter NavigationType: The type of navigation being performed. + /** Publishes navigation events + - Parameter NavigationEvent: The navigation event that is happening. + - Parameter NavigationOperation: The operation performing the navigation. */ - public let onNavigationWillBegin = PassthroughSubject<(NavigationOperation), Never>() - - /** Publishes when the navigation did finish - - Parameter NavigationType: The type of navigation being performed. - */ - public let onNavigationDidFinish = PassthroughSubject<(NavigationOperation), Never>() + public let onNavigation = PassthroughSubject<(NavigationEvent, NavigationOperation), Never>() // MARK: - Getters diff --git a/MVMCore/MVMCore/PresentationHandling/NavigationOperation.swift b/MVMCore/MVMCore/PresentationHandling/NavigationOperation.swift index 3292f7b..ad3e37d 100644 --- a/MVMCore/MVMCore/PresentationHandling/NavigationOperation.swift +++ b/MVMCore/MVMCore/PresentationHandling/NavigationOperation.swift @@ -116,7 +116,7 @@ open class NavigationOperation: MVMCoreOperation, UINavigationControllerDelegate } navigationController.delegate = self toNavigationControllerViewControllers = viewControllers - NavigationHandler.shared().onNavigationWillBegin.send(self) + NavigationHandler.shared().onNavigation.send((.willNavigate, self)) guard !checkAndHandleForCancellation() else { return } navigationController.setViewControllers(viewControllers, animated: animated) } @@ -175,11 +175,11 @@ open class NavigationOperation: MVMCoreOperation, UINavigationControllerDelegate */ @MainActor open func present(viewController: UIViewController, onController: UIViewController, animated: Bool = true) { - NavigationHandler.shared().onNavigationWillBegin.send(self) + NavigationHandler.shared().onNavigation.send((.willNavigate, self)) guard !checkAndHandleForCancellation() else { return } viewController.modalPresentationStyle = .fullScreen onController.getViewControllerToPresentOn().present(viewController, animated: animated) { [self] in - NavigationHandler.shared().onNavigationDidFinish.send(self) + NavigationHandler.shared().onNavigation.send((.didNavigate, self)) markAsFinished() } } @@ -190,10 +190,10 @@ open class NavigationOperation: MVMCoreOperation, UINavigationControllerDelegate */ @MainActor open func dismiss(viewController: UIViewController, animated: Bool = true) { - NavigationHandler.shared().onNavigationWillBegin.send(self) + NavigationHandler.shared().onNavigation.send((.willNavigate, self)) guard !checkAndHandleForCancellation() else { return } viewController.dismiss(animated: animated) { [self] in - NavigationHandler.shared().onNavigationDidFinish.send(self) + NavigationHandler.shared().onNavigation.send((.didNavigate, self)) markAsFinished() } } @@ -202,7 +202,7 @@ open class NavigationOperation: MVMCoreOperation, UINavigationControllerDelegate open func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) { delegate?.navigationController?(navigationController, didShow: viewController, animated: animated) - NavigationHandler.shared().onNavigationDidFinish.send(self) + NavigationHandler.shared().onNavigation.send((.didNavigate, self)) markAsFinished() } @@ -231,3 +231,58 @@ open class NavigationOperation: MVMCoreOperation, UINavigationControllerDelegate return animatedTransitioning } } + +fileprivate struct KVOPublisher: Publisher { + public typealias Output = ValueType + public typealias Failure = Never + + private let object: ObjectType + private let keyPath: String + private let options: NSKeyValueObservingOptions + + public init(object: ObjectType, keyPath: String, options: NSKeyValueObservingOptions = [.new]) { + self.object = object + self.keyPath = keyPath + self.options = options + } + + public func receive(subscriber: S) where ValueType == S.Input, Never == S.Failure { + let subscription = KVOSubscription(subscriber: subscriber, object: object, keyPath: keyPath, options: options) + subscriber.receive(subscription: subscription) + } + + private final class KVOSubscription:NSObject, Subscription where S.Input == ValueType { + private let subscriber: S + private var object: ObjectType + private var keyPath: String + private var options: NSKeyValueObservingOptions + + init(subscriber: S, object: ObjectType, keyPath: String, options: NSKeyValueObservingOptions = [.new]) { + self.object = object + self.keyPath = keyPath + self.options = options + self.subscriber = subscriber + super.init() + self.object.addObserver(self, forKeyPath: self.keyPath, options: options, context: nil) + } + + func request(_ demand: Subscribers.Demand) {} + func cancel() {} + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == self.keyPath, let change = change, let newValue = change[.newKey] as? ValueType { + _ = subscriber.receive(newValue) + } + } + + deinit { + self.object.removeObserver(self, forKeyPath: keyPath) + } + } +} + +extension NSObject { + fileprivate func kvoPublisher(for keyPath: String, type: Value.Type, options: NSKeyValueObservingOptions = [.new]) -> AnyPublisher { + return KVOPublisher(object: self, keyPath: keyPath, options: options).eraseToAnyPublisher() + } +} diff --git a/MVMCore/MVMCore/Utility/Helpers/KVOPublisher.swift b/MVMCore/MVMCore/Utility/Helpers/KVOPublisher.swift deleted file mode 100644 index 8640691..0000000 --- a/MVMCore/MVMCore/Utility/Helpers/KVOPublisher.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// KVOPublisher.swift -// MVMCore -// -// Created by Scott Pfeil on 8/14/23. -// Copyright © 2023 myverizon. All rights reserved. -// - -import Foundation -import Combine - -public struct KVOPublisher: Publisher { - public typealias Output = ValueType - public typealias Failure = Never - - private let object: ObjectType - private let keyPath: String - private let options: NSKeyValueObservingOptions - - public init(object: ObjectType, keyPath: String, options: NSKeyValueObservingOptions = [.new]) { - self.object = object - self.keyPath = keyPath - self.options = options - } - - public func receive(subscriber: S) where ValueType == S.Input, Never == S.Failure { - let subscription = KVOSubscription(subscriber: subscriber, object: object, keyPath: keyPath, options: options) - subscriber.receive(subscription: subscription) - } - - private final class KVOSubscription:NSObject, Subscription where S.Input == ValueType { - private let subscriber: S - private var object: ObjectType - private var keyPath: String - private var options: NSKeyValueObservingOptions - - init(subscriber: S, object: ObjectType, keyPath: String, options: NSKeyValueObservingOptions = [.new]) { - self.object = object - self.keyPath = keyPath - self.options = options - self.subscriber = subscriber - super.init() - self.object.addObserver(self, forKeyPath: self.keyPath, options: options, context: nil) - } - - func request(_ demand: Subscribers.Demand) {} - func cancel() {} - - override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { - if keyPath == self.keyPath, let change = change, let newValue = change[.newKey] as? ValueType { - _ = subscriber.receive(newValue) - } - } - - deinit { - self.object.removeObserver(self, forKeyPath: keyPath) - } - } -} - -extension NSObject { - public func kvoPublisher(for keyPath: String, type: Value.Type, options: NSKeyValueObservingOptions = [.new]) -> AnyPublisher { - return KVOPublisher(object: self, keyPath: keyPath, options: options).eraseToAnyPublisher() - } -}