Slather logo

Coverage for "UIViewController+Extension.swift" : 100.00%

(63 of 63 relevant lines covered)

RouteComposer/Classes/Extensions/UIViewController+Extension.swift

1
//
2
// RouteComposer
3
// UIViewController+Extension.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
public extension UIViewController {
16
17
    /// Iterates through the view controller stack to finds a `UIViewController` instance.
18
    ///
19
    /// - Parameters:
20
    ///   - viewController: A `UIViewController` instance to start from.
21
    ///   - options: A combination of `SearchOptions`.
22
    ///   - containerAdapterLocator: A `ContainerAdapterLocator` instance.
23
    ///   - predicate: A block that should return `true` if the `UIViewController` instance provided is the
24
    ///     one that is being searched for.
25
    /// - Returns: A `UIViewController` instance if found, `nil` otherwise.
26
    static func findViewController(in viewController: UIViewController,
27
                                   options: SearchOptions = .currentAndUp,
28
                                   containerAdapterLocator: ContainerAdapterLocator = RouteComposerDefaults.shared.containerAdapterLocator,
29
                                   using predicate: (UIViewController) -> Bool) throws -> UIViewController? {
1360x
30
        guard !viewController.isBeingDismissed else {
1360x
31
            return nil
2x
32
        }
1350x
33
1350x
34
        if options.contains(.current), predicate(viewController) {
1350x
35
            return viewController
322x
36
        }
1030x
37
1030x
38
        if options.contains(.parent),
1030x
39
           let parentViewController = viewController.parentViewController,
1030x
40
           let foundViewController = try findViewController(in: parentViewController,
1030x
41
                                                            options: [.current, .parent],
1030x
42
                                                            containerAdapterLocator: containerAdapterLocator,
1030x
43
                                                            using: predicate) {
1030x
44
            return foundViewController
6x
45
        }
1030x
46
1030x
47
        if let container = viewController as? ContainerViewController, options.contains(.visible) || options.contains(.contained) {
1030x
48
            var viewControllers: [[UIViewController]] = []
660x
49
            let containerAdapter = try containerAdapterLocator.getAdapter(for: container)
660x
50
            viewControllers.append(containerAdapter.visibleViewControllers)
660x
51
            if options.contains(.contained) {
660x
52
                viewControllers.append(containerAdapter.containedViewControllers)
336x
53
            }
660x
54
            for currentViewController in Array(viewControllers.joined()).uniqueElements() {
809x
55
                var internalOptions: SearchOptions = options.contains(.visible) ? .currentVisibleOnly : [.current, .contained]
809x
56
                if options.contains(.presented) {
809x
57
                    internalOptions.insert(.presented)
320x
58
                }
809x
59
                if let foundViewController = try findViewController(in: currentViewController,
809x
60
                                                                    options: internalOptions,
809x
61
                                                                    containerAdapterLocator: containerAdapterLocator,
809x
62
                                                                    using: predicate) {
809x
63
                    return foundViewController
412x
64
                }
412x
65
            }
397x
66
        }
619x
67
619x
68
        if options.contains(.presented),
619x
69
           let presentedViewController = viewController.presentedViewController {
619x
70
            let presentedOptions = options.subtracting(.presenting)
18x
71
            if let foundViewController = try findViewController(in: presentedViewController,
18x
72
                                                                options: presentedOptions,
18x
73
                                                                containerAdapterLocator: containerAdapterLocator,
18x
74
                                                                using: predicate) {
18x
75
                return foundViewController
3x
76
            }
15x
77
        }
616x
78
616x
79
        if options.contains(.presenting),
616x
80
           let presentingViewController = viewController.presentingViewController {
616x
81
            let presentingOptions = options.subtracting(.presented)
52x
82
            if let foundViewController = try findViewController(in: presentingViewController,
52x
83
                                                                options: presentingOptions,
52x
84
                                                                containerAdapterLocator: containerAdapterLocator,
52x
85
                                                                using: predicate) {
52x
86
                return foundViewController
6x
87
            }
46x
88
        }
610x
89
610x
90
        return nil
610x
91
    }
616x
92
93
}