Slather logo

Coverage for "SingleNavigationRouter.swift" : 100.00%

(38 of 38 relevant lines covered)

RouteComposer/Classes/Extra/SingleNavigationRouter.swift

1
//
2
// RouteComposer
3
// SingleNavigationRouter.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
/// Lock object to be shared between `SingleNavigationRouter` instances.
17
public final class SingleNavigationLock {
18
19
    private final var isNavigationInProgressFlag = false
20
21
    /// `SingleNavigationLock` state
22
    public final var isNavigationInProgress: Bool {
7x
23
        isNavigationInProgressFlag
7x
24
    }
7x
25
26
    /// Constructor
27
    public init() {}
19x
28
29
    final func startNavigation() {
3x
30
        isNavigationInProgressFlag = true
3x
31
    }
3x
32
33
    final func stopNavigation() {
3x
34
        isNavigationInProgressFlag = false
3x
35
    }
3x
36
37
}
38
39
/// The `Router` proxy guarantees that not more than one navigation will happen simultaneously.
40
///
41
/// It is useful to avoid situations when the application can not control the amount of navigations
42
/// (for example, a navigation triggered by the push notifications)
43
public struct SingleNavigationRouter<R>: Router where R: Router {
44
45
    // MARK: Properties
46
47
    var router: R
48
49
    /// Shared `SingleNavigationLock` instance
50
    public let lock: SingleNavigationLock
51
52
    // MARK: Methods
53
54
    /// Constructor
55
    ///
56
    /// - Parameters:
57
    ///   - router: `Router` instance to proxy.
58
    ///   - lock: Shared `SingleNavigationLock` instance.
59
    public init(router: R, lock: SingleNavigationLock) {
18x
60
        self.router = router
18x
61
        self.lock = lock
18x
62
    }
18x
63
64
    public func navigate<Context>(to step: DestinationStep<some UIViewController, Context>,
65
                                  with context: Context,
66
                                  animated: Bool,
67
                                  completion: ((RoutingResult) -> Void)?) throws {
3x
68
        guard !lock.isNavigationInProgress else {
3x
69
            throw RoutingError.compositionFailed(.init("Navigation is in progress"))
1x
70
        }
2x
71
        lock.startNavigation()
2x
72
        do {
2x
73
            try router.navigate(to: step, with: context, animated: animated, completion: { success in
2x
74
                self.lock.stopNavigation()
1x
75
                completion?(success)
1x
76
            })
1x
77
        } catch {
2x
78
            lock.stopNavigation()
1x
79
            throw error
1x
80
        }
2x
81
    }
2x
82
83
}
84
85
extension SingleNavigationRouter: InterceptableRouter where R: InterceptableRouter {
86
87
    public mutating func add<RI: RoutingInterceptor>(_ interceptor: RI) where RI.Context == Any? {
1x
88
        router.add(interceptor)
1x
89
    }
1x
90
91
    public mutating func add<CT: ContextTask>(_ contextTask: CT) where CT.ViewController == UIViewController, CT.Context == Any? {
1x
92
        router.add(contextTask)
1x
93
    }
1x
94
95
    public mutating func add<PT: PostRoutingTask>(_ postTask: PT) where PT.Context == Any? {
1x
96
        router.add(postTask)
1x
97
    }
1x
98
99
}