Slather logo

Coverage for "CATransaction+Action.swift" : 100.00%

(45 of 45 relevant lines covered)

RouteComposer/Classes/Extra/CATransaction+Action.swift

1
//
2
// RouteComposer
3
// CATransaction+Action.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
/// Extension that wraps an action into `CATransaction`.
17
///
18
/// When `DefaultRouter` builds a complicated chain of animated modifications in the `UIViewController`s stack
19
/// it might be necessary to wrap some actions into single `CATransaction`.
20
///
21
/// *Example: When `UINavigationController` does pop, then push of the `UIViewController` and then tries to
22
/// do some to it or find it with a `Finder`. Then `DefaultRouter` has to wait till the end of the animation.*
23
public extension CATransaction {
24
25
    /// Wraps `Action` in to `CATransaction`
26
    ///
27
    /// - Parameter action: `Action` instance
28
    static func wrap<A: Action>(_ action: A) -> CATransactionWrappedAction<A> {
60x
29
        CATransactionWrappedAction(action)
60x
30
    }
60x
31
32
    /// Wraps `ContainerAction` in to `CATransaction`
33
    ///
34
    /// - Parameter action: `ContainerAction` instance
35
    static func wrap<A: ContainerAction>(_ action: A) -> CATransactionWrappedContainerAction<A> {
5x
36
        CATransactionWrappedContainerAction(action)
5x
37
    }
5x
38
39
}
40
41
/// `CATransaction` wrapper for `Action`
42
public struct CATransactionWrappedAction<A: Action>: Action {
43
44
    // MARK: Associated types
45
46
    /// Type of the `UIViewController` that `Action` can start from.
47
    public typealias ViewController = A.ViewController
48
49
    // MARK: Properties
50
51
    let action: A
52
53
    // MARK: Methods
54
55
    init(_ action: A) {
60x
56
        self.action = action
60x
57
    }
60x
58
59
    public func perform(with viewController: UIViewController, on existingController: A.ViewController, animated: Bool, completion: @escaping (RoutingResult) -> Void) {
17x
60
        guard animated else {
17x
61
            action.perform(with: viewController, on: existingController, animated: false, completion: completion)
1x
62
            return
1x
63
        }
16x
64
        CATransaction.begin()
16x
65
        var actionResult: RoutingResult = .failure(RoutingError.compositionFailed(.init("Wrapped \(action) did not complete correctly.")))
16x
66
        action.perform(with: viewController, on: existingController, animated: true, completion: { result in
16x
67
            actionResult = result
16x
68
        })
16x
69
        CATransaction.setCompletionBlock {
16x
70
            completion(actionResult)
16x
71
        }
16x
72
        CATransaction.commit()
16x
73
    }
16x
74
75
}
76
77
/// `CATransaction` wrapper for `ContainerAction`
78
public struct CATransactionWrappedContainerAction<A: ContainerAction>: ContainerAction {
79
80
    // MARK: Associated types
81
82
    /// Type of the `UIViewController` that `Action` can start from.
83
    public typealias ViewController = A.ViewController
84
85
    // MARK: Properties
86
87
    let action: A
88
89
    // MARK: Methods
90
91
    init(_ action: A) {
5x
92
        self.action = action
5x
93
    }
5x
94
95
    public func perform(embedding viewController: UIViewController, in childViewControllers: inout [UIViewController]) throws {
2x
96
        try action.perform(embedding: viewController, in: &childViewControllers)
2x
97
    }
2x
98
99
    public func perform(with viewController: UIViewController, on existingController: A.ViewController, animated: Bool, completion: @escaping (RoutingResult) -> Void) {
2x
100
        guard animated else {
2x
101
            action.perform(with: viewController, on: existingController, animated: false, completion: completion)
1x
102
            return
1x
103
        }
1x
104
        CATransaction.begin()
1x
105
        var actionResult: RoutingResult = .failure(RoutingError.compositionFailed(.init("Wrapped \(action) did not complete correctly.")))
1x
106
        action.perform(with: viewController, on: existingController, animated: true, completion: { result in
1x
107
            actionResult = result
1x
108
        })
1x
109
        CATransaction.setCompletionBlock {
1x
110
            completion(actionResult)
1x
111
        }
1x
112
        CATransaction.commit()
1x
113
    }
1x
114
115
}