Slather logo

Coverage for "DefaultStackIterator.swift" : 81.82%

(27 of 33 relevant lines covered)

RouteComposer/Classes/Finders/Stack Iterator/DefaultStackIterator.swift

1
//
2
// RouteComposer
3
// DefaultStackIterator.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 Foundation
14
import UIKit
15
16
/// Default implementation of `StackIterator` protocol
17
public struct DefaultStackIterator: StackIterator {
18
19
    // MARK: Internal entities
20
21
    /// A starting point in the `UIViewController`s stack
22
    ///
23
    /// - topMost: Start from the topmost `UIViewController`
24
    /// - root: Start from the `UIWindow`s root `UIViewController`
25
    /// - custom: Start from the custom `UIViewController`
26
    public enum StartingPoint {
27
28
        /// Start from the topmost `UIViewController`
29
        case topmost
30
31
        /// Start from the `UIWindow`s root `UIViewController`
32
        case root
33
34
        /// Start from the custom `UIViewController`
35
        case custom(@autoclosure () throws -> UIViewController?)
36
37
    }
38
39
    // MARK: Properties
40
41
    /// `SearchOptions` to be used by `StackIteratingFinder`
42
    public let options: SearchOptions
43
44
    /// A starting point in the `UIViewController`s stack
45
    public let startingPoint: StartingPoint
46
47
    /// `WindowProvider` to get proper `UIWindow`
48
    public let windowProvider: WindowProvider
49
50
    /// `ContainerAdapter` instance.
51
    public let containerAdapterLocator: ContainerAdapterLocator
52
53
    // MARK: Methods
54
55
    /// Constructor
56
    public init(options: SearchOptions = .fullStack,
57
                startingPoint: StartingPoint = .topmost,
58
                windowProvider: WindowProvider,
59
                containerAdapterLocator: ContainerAdapterLocator) {
113x
60
        self.startingPoint = startingPoint
113x
61
        self.options = options
113x
62
        self.windowProvider = windowProvider
113x
63
        self.containerAdapterLocator = containerAdapterLocator
113x
64
    }
113x
65
66
    /// Deprecated Constructor.
67
    /// May create a conflict with default configuration. Please use `DefaultStackIterator.init(options:startingPoint:windowProvider:containerAdapterLocator:)`.
68
    @available(*, deprecated, message: "May create a conflict with default configuration. Please use init(options:startingPoint:windowProvider:containerAdapterLocator:)")
69
    public init(options: SearchOptions = .fullStack,
70
                startingPoint: StartingPoint = .topmost) {
!
71
        self.startingPoint = startingPoint
!
72
        self.options = options
!
73
        self.windowProvider = RouteComposerDefaults.shared.windowProvider
!
74
        self.containerAdapterLocator = RouteComposerDefaults.shared.containerAdapterLocator
!
75
    }
!
76
77
    /// Returns `UIViewController` instance if found
78
    ///
79
    /// - Parameter predicate: A block that contains `UIViewController` matching condition
80
    public func firstViewController(where predicate: (UIViewController) -> Bool) throws -> UIViewController? {
193x
81
        guard let rootViewController = try getStartingViewController(),
193x
82
              let viewController = try UIViewController.findViewController(in: rootViewController,
193x
83
                                                                           options: options,
193x
84
                                                                           containerAdapterLocator: containerAdapterLocator,
193x
85
                                                                           using: predicate) else {
193x
86
            return nil
96x
87
        }
97x
88
97x
89
        return viewController
97x
90
    }
193x
91
92
    func getStartingViewController() throws -> UIViewController? {
197x
93
        switch startingPoint {
197x
94
        case .topmost:
197x
95
            return windowProvider.window?.topmostViewController
165x
96
        case .root:
197x
97
            return windowProvider.window?.rootViewController
21x
98
        case let .custom(viewControllerClosure):
197x
99
            return try viewControllerClosure()
11x
100
        }
197x
101
    }
197x
102
103
}