Slather logo

Coverage for "DefaultStackPresentationHandler.swift" : 91.53%

(54 of 59 relevant lines covered)

RouteComposer/Classes/Router/Helpers/DefaultStackPresentationHandler.swift

1
//
2
// RouteComposer
3
// DefaultStackPresentationHandler.swift
4
// https://github.com/ekazaev/route-composer
5
//
6
// Created by Eugene Kazaev in 2018-2022.
7
// Distributed under the MIT license.
8
//
9
// Become a sponsor:
10
// https://github.com/sponsors/ekazaev
11
//
12
13
import UIKit
14
15
/// Default implementation of `StackPresentationHandler`
16
public struct DefaultStackPresentationHandler: StackPresentationHandler, MainThreadChecking {
17
18
    // MARK: Properties
19
20
    /// `Logger` instance to be used by `DefaultRouter`.
21
    public let logger: Logger?
22
23
    /// `ContainerAdapter` instance.
24
    public let containerAdapterLocator: ContainerAdapterLocator
25
26
    // MARK: Methods
27
28
    /// Constructor
29
    ///
30
    /// Parameters
31
    ///   - logger: A `Logger` instance to be used by the `DefaultRouter`.
32
    ///   - containerAdapterLocator: A `ContainerAdapterLocator` instance to be used by the `DefaultRouter`.
33
    public init(logger: Logger? = RouteComposerDefaults.shared.logger,
34
                containerAdapterLocator: ContainerAdapterLocator = RouteComposerDefaults.shared.containerAdapterLocator) {
45x
35
        self.logger = logger
45x
36
        self.containerAdapterLocator = containerAdapterLocator
45x
37
    }
45x
38
39
    public func dismissPresented(from viewController: UIViewController, animated: Bool, completion: @escaping (_: RoutingResult) -> Void) {
103x
40
        assertIfNotMainThread(logger: logger)
103x
41
        if let presentedController = viewController.presentedViewController {
103x
42
            if !presentedController.isBeingDismissed {
10x
43
                viewController.dismiss(animated: animated) {
10x
44
                    self.logger?.log(.info("Dismissed all the view controllers presented from \(String(describing: viewController))"))
10x
45
                    completion(.success)
10x
46
                }
10x
47
            } else {
10x
48
                completion(.failure(RoutingError.compositionFailed(.init("Attempt to dismiss \(String(describing: presentedController)) while it is being dismissed"))))
!
49
            }
10x
50
        } else {
103x
51
            completion(.success)
93x
52
        }
103x
53
    }
103x
54
55
    public func makeVisibleInParentContainers(_ viewController: UIViewController,
56
                                              animated: Bool,
57
                                              completion: @escaping (RoutingResult) -> Void) {
198x
58
        var parentViewControllers = viewController.allParents
198x
59
        let topParentViewController = parentViewControllers.last
198x
60
        func makeVisible(viewController: UIViewController, completion: @escaping (RoutingResult) -> Void) {
354x
61
            assertIfNotMainThread(logger: logger)
354x
62
            guard !parentViewControllers.isEmpty else {
354x
63
                if !animated,
198x
64
                   let topParentViewController,
198x
65
                   topParentViewController.isViewLoaded {
198x
66
                    topParentViewController.view.layoutIfNeeded()
2x
67
                }
198x
68
                completion(.success)
198x
69
                return
198x
70
            }
198x
71
            do {
156x
72
                let parentViewController = parentViewControllers.removeFirst()
156x
73
                if let container = parentViewController as? ContainerViewController {
156x
74
                    let containerAdapter = try containerAdapterLocator.getAdapter(for: container)
156x
75
                    guard !containerAdapter.isVisible(viewController) else {
156x
76
                        logger?.log(.info("View controller \(String(describing: viewController)) is visible in \(String(describing: container)). No action needed."))
113x
77
                        return makeVisible(viewController: parentViewController, completion: completion)
113x
78
                    }
113x
79
                    containerAdapter.makeVisible(viewController, animated: animated, completion: { result in
43x
80
                        guard result.isSuccessful else {
43x
81
                            completion(result)
!
82
                            return
!
83
                        }
43x
84
                        self.logger?.log(.info("Made \(String(describing: viewController)) visible in \(String(describing: container))"))
43x
85
                        makeVisible(viewController: parentViewController, completion: completion)
43x
86
                    })
43x
87
                } else {
43x
88
                    makeVisible(viewController: parentViewController, completion: completion)
!
89
                }
43x
90
            } catch {
43x
91
                completion(.failure(error))
!
92
            }
43x
93
        }
43x
94
198x
95
        makeVisible(viewController: viewController, completion: completion)
198x
96
    }
198x
97
98
}