| 1 |
//
|
|
| 2 |
// RouteComposer
|
|
| 3 |
// ContainerActionBox.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 |
struct ContainerActionBox<A: ContainerAction>: AnyAction, AnyActionBox, CustomStringConvertible, MainThreadChecking {
|
|
| 17 |
|
|
| 18 |
let action: A
|
|
| 19 |
|
|
| 20 |
init(_ action: A) {
|
126x |
| 21 |
self.action = action
|
126x |
| 22 |
}
|
126x |
| 23 |
|
|
| 24 |
func perform(with viewController: UIViewController,
|
|
| 25 |
on existingController: UIViewController,
|
|
| 26 |
with postponedIntegrationHandler: PostponedActionIntegrationHandler,
|
|
| 27 |
nextAction: AnyAction?,
|
|
| 28 |
animated: Bool,
|
|
| 29 |
completion: @escaping (RoutingResult) -> Void) {
|
30x |
| 30 |
assertIfNotMainThread()
|
30x |
| 31 |
if let postponedController = postponedIntegrationHandler.containerViewController {
|
30x |
| 32 |
guard postponedController is A.ViewController else {
|
1x |
| 33 |
postponedIntegrationHandler.purge(animated: animated, completion: { result in
|
! |
| 34 |
guard result.isSuccessful else {
|
! |
| 35 |
completion(result)
|
! |
| 36 |
return
|
! |
| 37 |
}
|
! |
| 38 |
self.perform(with: viewController,
|
! |
| 39 |
on: existingController,
|
! |
| 40 |
with: postponedIntegrationHandler,
|
! |
| 41 |
nextAction: nextAction,
|
! |
| 42 |
animated: animated,
|
! |
| 43 |
completion: completion)
|
! |
| 44 |
})
|
! |
| 45 |
return
|
! |
| 46 |
}
|
1x |
| 47 |
embed(viewController: viewController, with: postponedIntegrationHandler, completion: completion)
|
1x |
| 48 |
} else {
|
30x |
| 49 |
guard let containerController: A.ViewController = UIViewController.findContainer(of: existingController) else {
|
29x |
| 50 |
completion(.failure(RoutingError.typeMismatch(type: type(of: existingController),
|
! |
| 51 |
expectedType: ActionType.ViewController.self,
|
! |
| 52 |
.init("Container of \(String(describing: ActionType.ViewController.self)) type cannot be found in the parents of " +
|
! |
| 53 |
"\(String(describing: existingController)) to perform \(action)"))))
|
! |
| 54 |
return
|
! |
| 55 |
}
|
29x |
| 56 |
let shouldDelayPerforming = nextAction?.isEmbeddable(to: A.ViewController.self) ?? false
|
29x |
| 57 |
if shouldDelayPerforming {
|
29x |
| 58 |
postponedIntegrationHandler.update(containerViewController: containerController, animated: animated, completion: { result in
|
2x |
| 59 |
guard result.isSuccessful else {
|
2x |
| 60 |
completion(result)
|
! |
| 61 |
return
|
! |
| 62 |
}
|
2x |
| 63 |
self.embed(viewController: viewController, with: postponedIntegrationHandler, completion: completion)
|
2x |
| 64 |
})
|
2x |
| 65 |
} else {
|
29x |
| 66 |
postponedIntegrationHandler.purge(animated: animated, completion: { result in
|
27x |
| 67 |
guard result.isSuccessful else {
|
27x |
| 68 |
completion(result)
|
! |
| 69 |
return
|
! |
| 70 |
}
|
27x |
| 71 |
self.action.perform(with: viewController, on: containerController, animated: animated) { result in
|
27x |
| 72 |
self.assertIfNotMainThread()
|
27x |
| 73 |
completion(result)
|
27x |
| 74 |
}
|
27x |
| 75 |
})
|
27x |
| 76 |
}
|
29x |
| 77 |
}
|
30x |
| 78 |
}
|
30x |
| 79 |
|
|
| 80 |
private func embed(viewController: UIViewController, with postponedIntegrationHandler: PostponedActionIntegrationHandler, completion: @escaping (RoutingResult) -> Void) {
|
3x |
| 81 |
do {
|
3x |
| 82 |
var postponedChildControllers = postponedIntegrationHandler.postponedViewControllers
|
3x |
| 83 |
try perform(embedding: viewController, in: &postponedChildControllers)
|
3x |
| 84 |
postponedIntegrationHandler.update(postponedViewControllers: postponedChildControllers)
|
3x |
| 85 |
completion(.success)
|
3x |
| 86 |
} catch {
|
3x |
| 87 |
completion(.failure(error))
|
! |
| 88 |
}
|
3x |
| 89 |
}
|
3x |
| 90 |
|
|
| 91 |
func perform(embedding viewController: UIViewController, in childViewControllers: inout [UIViewController]) throws {
|
67x |
| 92 |
try action.perform(embedding: viewController, in: &childViewControllers)
|
67x |
| 93 |
}
|
67x |
| 94 |
|
|
| 95 |
public var description: String {
|
28x |
| 96 |
String(describing: action)
|
28x |
| 97 |
}
|
28x |
| 98 |
|
|
| 99 |
func isEmbeddable(to container: ContainerViewController.Type) -> Bool {
|
40x |
| 100 |
container is A.ViewController.Type
|
40x |
| 101 |
}
|
40x |
| 102 |
|
|
| 103 |
}
|
|