From 63777fef993373f9ed773f357e4eba4a28c5183c Mon Sep 17 00:00:00 2001 From: Nikita Semenov Date: Sun, 22 Jan 2023 20:09:24 +0300 Subject: [PATCH] fix: update an approach to handling operation in TIDeeplinkService --- .../BaseNavigationStackDeeplinkHandler.swift | 16 ++-- .../DeeplinkHandler/DeeplinkHandler.swift | 3 +- .../UIViewController+DeeplinkHandler.swift | 30 +++++--- .../RootViewControllerKeeper.swift} | 7 +- TIDeepLink/Sources/DeeplinkMapper.swift | 2 + TIDeeplink/Sources/TIDeeplinkService.swift | 76 +++++++++++-------- 6 files changed, 83 insertions(+), 51 deletions(-) rename TIDeepLink/Sources/{DeeplinkType.swift => DeeplinkHandler/RootViewControllerKeeper.swift} (85%) diff --git a/TIDeepLink/Sources/DeeplinkHandler/BaseNavigationStackDeeplinkHandler.swift b/TIDeepLink/Sources/DeeplinkHandler/BaseNavigationStackDeeplinkHandler.swift index db7f8671..fd8e4831 100644 --- a/TIDeepLink/Sources/DeeplinkHandler/BaseNavigationStackDeeplinkHandler.swift +++ b/TIDeepLink/Sources/DeeplinkHandler/BaseNavigationStackDeeplinkHandler.swift @@ -23,14 +23,15 @@ import Foundation import UIKit -open class BaseNavigationStackDeeplinkHandler: DeeplinkHandler { +open class BaseNavigationStackDeeplinkHandler: DeeplinkHandler { + + public typealias DeeplinkType = ControllerKeeper.DeeplinkHandler.DeeplinkType + public typealias Handler = ControllerKeeper.DeeplinkHandler + + public var rootViewControllerKeeper: ControllerKeeper? // MARK: - DeeplinkHandler - open func canHandle(deeplink: DeeplinkType) -> Bool { - findHandler(for: deeplink) != nil - } - open func handle(deeplink: DeeplinkType) -> Operation? { let handler = findHandler(for: deeplink) return handler?.handle(deeplink: deeplink) @@ -38,8 +39,9 @@ open class BaseNavigationStackDeeplinkHandler: DeeplinkHandler { // MARK: - Open methods - open func findHandler(for deeplink: DeeplinkType) -> DeeplinkHandler? { - guard let rootController = UIApplication.shared.keyWindow?.rootViewController else { + open func findHandler(for deeplink: DeeplinkType) -> Handler? { + + guard let rootController = rootViewControllerKeeper?.rootDeeplinkHandlerController else { return nil } diff --git a/TIDeepLink/Sources/DeeplinkHandler/DeeplinkHandler.swift b/TIDeepLink/Sources/DeeplinkHandler/DeeplinkHandler.swift index bb874296..cebab7c8 100644 --- a/TIDeepLink/Sources/DeeplinkHandler/DeeplinkHandler.swift +++ b/TIDeepLink/Sources/DeeplinkHandler/DeeplinkHandler.swift @@ -23,6 +23,7 @@ import Foundation public protocol DeeplinkHandler { - func canHandle(deeplink: DeeplinkType) -> Bool + associatedtype DeeplinkType: Hashable + func handle(deeplink: DeeplinkType) -> Operation? } diff --git a/TIDeepLink/Sources/DeeplinkHandler/Helpers/UIViewController+DeeplinkHandler.swift b/TIDeepLink/Sources/DeeplinkHandler/Helpers/UIViewController+DeeplinkHandler.swift index 9c15d0fd..c8bbd46f 100644 --- a/TIDeepLink/Sources/DeeplinkHandler/Helpers/UIViewController+DeeplinkHandler.swift +++ b/TIDeepLink/Sources/DeeplinkHandler/Helpers/UIViewController+DeeplinkHandler.swift @@ -26,33 +26,40 @@ public typealias DeeplinkHandlerViewController = DeeplinkHandler & UIViewControl public extension UIViewController { - func findHandler(for deeplink: DeeplinkType) -> DeeplinkHandlerViewController? { - if let deeplinksHandler = self as? DeeplinkHandlerViewController, - deeplinksHandler.canHandle(deeplink: deeplink) { + func findHandler( + for deeplink: DeeplinkHandler.DeeplinkType + ) -> DeeplinkHandler? { + + if let deeplinksHandler = self as? DeeplinkHandler, + let _ = deeplinksHandler.handle(deeplink: deeplink) { return deeplinksHandler } - if let deeplinksHandler = presentedViewController?.findHandler(for: deeplink) { + if let deeplinksHandler: DeeplinkHandler = presentedViewController?.findHandler(for: deeplink) { return deeplinksHandler } return findHandlerInViewHierarchy(for: deeplink) } - private func findHandlerInViewHierarchy(for deeplink: DeeplinkType) -> DeeplinkHandlerViewController? { + private func findHandlerInViewHierarchy( + for deeplink: DeeplinkHandler.DeeplinkType + ) -> DeeplinkHandler? { + switch self { case let navController as UINavigationController: - return navController.viewControllers.reversed().findHadler(for: deeplink) + let deeplinksHandler: DeeplinkHandler? = navController.viewControllers.reversed().findHandler(for: deeplink) + return deeplinksHandler case let tabController as UITabBarController: - if let deeplinksHandler = tabController.selectedViewController?.findHandler(for: deeplink) { + if let deeplinksHandler: DeeplinkHandler = tabController.selectedViewController?.findHandler(for: deeplink) { return deeplinksHandler } else if var tabControllers = tabController.viewControllers { if tabController.selectedIndex != NSNotFound { tabControllers.remove(at: tabController.selectedIndex) } - if let deeplinksHandler = tabControllers.findHadler(for: deeplink) { + if let deeplinksHandler: DeeplinkHandler = tabControllers.findHandler(for: deeplink) { return deeplinksHandler } } @@ -66,9 +73,12 @@ public extension UIViewController { } private extension Sequence where Element: UIViewController { - func findHadler(for deeplink: DeeplinkType) -> DeeplinkHandlerViewController? { + func findHandler( + for deeplink: DeeplinkHandler.DeeplinkType + ) -> DeeplinkHandler? { + for controller in self { - if let deeplinksHandler = controller.findHandler(for: deeplink) { + if let deeplinksHandler: DeeplinkHandler = controller.findHandler(for: deeplink) { return deeplinksHandler } } diff --git a/TIDeepLink/Sources/DeeplinkType.swift b/TIDeepLink/Sources/DeeplinkHandler/RootViewControllerKeeper.swift similarity index 85% rename from TIDeepLink/Sources/DeeplinkType.swift rename to TIDeepLink/Sources/DeeplinkHandler/RootViewControllerKeeper.swift index 1d317f71..acbd1864 100644 --- a/TIDeepLink/Sources/DeeplinkType.swift +++ b/TIDeepLink/Sources/DeeplinkHandler/RootViewControllerKeeper.swift @@ -20,5 +20,10 @@ // THE SOFTWARE. // -public protocol DeeplinkType { +import UIKit + +public protocol RootViewControllerKeeper { + associatedtype DeeplinkHandler: DeeplinkHandlerViewController + + var rootDeeplinkHandlerController: DeeplinkHandler { get } } diff --git a/TIDeepLink/Sources/DeeplinkMapper.swift b/TIDeepLink/Sources/DeeplinkMapper.swift index 60f3262e..9ad21cf3 100644 --- a/TIDeepLink/Sources/DeeplinkMapper.swift +++ b/TIDeepLink/Sources/DeeplinkMapper.swift @@ -23,5 +23,7 @@ import Foundation public protocol DeeplinkMapper { + associatedtype DeeplinkType + func map(url: URL) -> DeeplinkType? } diff --git a/TIDeeplink/Sources/TIDeeplinkService.swift b/TIDeeplink/Sources/TIDeeplinkService.swift index 7aa6503b..c8099fe4 100644 --- a/TIDeeplink/Sources/TIDeeplinkService.swift +++ b/TIDeeplink/Sources/TIDeeplinkService.swift @@ -23,71 +23,83 @@ import Foundation import TIFoundationUtils -public final class TIDeeplinksService { - - public static let shared = TIDeeplinksService() +open class TIDeeplinksService where Mapper.DeeplinkType == Handler.DeeplinkType, + Mapper.DeeplinkType == DeeplinkType { // MARK: - Private properties - private let operationQueue = OperationQueue.main + private var operationQueue: OperationQueue - private var pendingDeeplink: DeeplinkType? - - private(set) var isProcessingDeeplink = false + private var deeplinkQueue: [DeeplinkType] = [] + private var inProcessingSet: Set = [] // MARK: - Public properties - public var deeplinkMapper: DeeplinkMapper? - public var deeplinkHandler: DeeplinkHandler? + public var deeplinkMapper: Mapper? + public var deeplinkHandler: Handler? // MARK: - Init - private init() { - + public init(operationQueue: OperationQueue = .main) { + self.operationQueue = operationQueue } - // MARK: - Public methods + // MARK: - Open methods - public func configure(mapper: DeeplinkMapper, handler: DeeplinkHandler) { + open func configure(mapper: Mapper, handler: Handler) { deeplinkMapper = mapper deeplinkHandler = handler } @discardableResult - public func deferredHandle(url: URL) -> Bool { - pendingDeeplink = deeplinkMapper?.map(url: url) - return pendingDeeplink != nil + open func deferredHandle(url: URL) -> Bool { + let deeplink = deeplinkMapper?.map(url: url) + + guard let deeplink = deeplink else { + return false + } + + deeplinkQueue.append(deeplink) + + return true } - public func reset() { + open func reset() { operationQueue.cancelAllOperations() - pendingDeeplink = nil - isProcessingDeeplink = false + deeplinkQueue.removeAll() + inProcessingSet.removeAll() } - public func tryHandle() { - guard let deeplink = pendingDeeplink, - deeplinkHandler?.canHandle(deeplink: deeplink) ?? false else { + open func tryHandle() { + guard !deeplinkQueue.isEmpty else { return } handle() } - public func handle() { - guard let deeplink = pendingDeeplink, - let lastOperation = deeplinkHandler?.handle(deeplink: deeplink) else { + open func handle() { + guard let deeplink = deeplinkQueue.first else { return } - operationQueue.addOperation { [weak self] in - self?.isProcessingDeeplink = true - self?.pendingDeeplink = nil + deeplinkQueue.remove(at: .zero) + + guard !inProcessingSet.contains(deeplink), + let operation = deeplinkHandler?.handle(deeplink: deeplink) else { + return } - operationQueue.addOperations(lastOperation.flattenDependencies + [lastOperation], - waitUntilFinished: false) - operationQueue.addOperation { [weak self] in - self?.isProcessingDeeplink = false + + inProcessingSet.formUnion([deeplink]) + + let competionOperation = BlockOperation { [weak self] in + self?.inProcessingSet.subtract([deeplink]) } + + competionOperation.addDependency(operation) + + competionOperation.add(to: operationQueue) } }