fix: update an approach to handling operation in TIDeeplinkService

This commit is contained in:
Nikita Semenov 2023-01-22 20:09:24 +03:00
parent caeded9561
commit 63777fef99
6 changed files with 83 additions and 51 deletions

View File

@ -23,14 +23,15 @@
import Foundation
import UIKit
open class BaseNavigationStackDeeplinkHandler: DeeplinkHandler {
open class BaseNavigationStackDeeplinkHandler<ControllerKeeper: RootViewControllerKeeper>: 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
}

View File

@ -23,6 +23,7 @@
import Foundation
public protocol DeeplinkHandler {
func canHandle(deeplink: DeeplinkType) -> Bool
associatedtype DeeplinkType: Hashable
func handle(deeplink: DeeplinkType) -> Operation?
}

View File

@ -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<DeeplinkHandler: DeeplinkHandlerViewController>(
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<DeeplinkHandler: DeeplinkHandlerViewController>(
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<DeeplinkHandler: DeeplinkHandlerViewController>(
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
}
}

View File

@ -20,5 +20,10 @@
// THE SOFTWARE.
//
public protocol DeeplinkType {
import UIKit
public protocol RootViewControllerKeeper {
associatedtype DeeplinkHandler: DeeplinkHandlerViewController
var rootDeeplinkHandlerController: DeeplinkHandler { get }
}

View File

@ -23,5 +23,7 @@
import Foundation
public protocol DeeplinkMapper {
associatedtype DeeplinkType
func map(url: URL) -> DeeplinkType?
}

View File

@ -23,71 +23,83 @@
import Foundation
import TIFoundationUtils
public final class TIDeeplinksService {
public static let shared = TIDeeplinksService()
open class TIDeeplinksService<DeeplinkType: Hashable,
Mapper: DeeplinkMapper,
Handler: DeeplinkHandler> 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<DeeplinkType> = []
// 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)
}
}