|
| 1 | + |
| 2 | +## Overview |
| 3 | +Additions library provides an ordered way to perform application boot tasks. The main components of the boot process are: |
| 4 | + |
| 5 | +* [AppTasks](#apptasks) |
| 6 | +* [ServiceProvider](#serviceprovider) |
| 7 | +* [AppTask](#apptask) |
| 8 | +* [NoOpTask](#nooptask) |
| 9 | + |
| 10 | +Here's example sequence diagram from an app launched using `AppTasks`: |
| 11 | + |
| 12 | + |
| 13 | + |
| 14 | +To understand better each step, keep on reading. |
| 15 | + |
| 16 | +## AppTasks |
| 17 | +### Description |
| 18 | +`AppTasks` provides an object that manages setup tasks when booting the application. When setup tasks are running, it buffers `AppDelegate` delegate method calls, when setup tasks are finished, it forwards buffered `AppDelegate` calls, and it keeps sending further `AppDelegate` calls to observing tasks. |
| 19 | + |
| 20 | +### Example |
| 21 | +You must create this object on your `SceneDelegate` or `AppDelegate` in order to forward application events. |
| 22 | + |
| 23 | +```swift |
| 24 | +class AppDelegate: UIResponder, UIApplicationDelegate { |
| 25 | + |
| 26 | + private var appServices = AppServices() |
| 27 | + /// In this case we are getting an instance of `AppTasks` by building it, but after it is built, we can also get it by calling: `AppTasks.shared` |
| 28 | + private lazy var tasks = AppTasks.build(serviceProviders: [AdditionsServices(), appServices]) { |
| 29 | + print("all tasks completed") |
| 30 | + } |
| 31 | + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { |
| 32 | + |
| 33 | + /// Calls to `didFinishLaunching` and other delegate methods must be redirected to the array of `tasks` we received from building an `AppTasks` object, so that they can be buffered and forwarded to the currently registered tasks. We will do the same for all `AppDeleagte` delegate calls. |
| 34 | + tasks.forEach { |
| 35 | + _ = $0.application?(application, didFinishLaunchingWithOptions: launchOptions) |
| 36 | + } |
| 37 | + return true |
| 38 | + } |
| 39 | +} |
| 40 | +``` |
| 41 | + |
| 42 | +## ServiceProvider |
| 43 | +### Description |
| 44 | +To take advantage of the `ServiceLocator` that `Additions` provides, you must create a subclass of `ServiceProvider`. It is in charge of building the necessary components for `AppTasks` to work. |
| 45 | + |
| 46 | +It is also where we’ll define in which order tasks must be ran. |
| 47 | + |
| 48 | +### Example |
| 49 | +```swift |
| 50 | +import Additions |
| 51 | + |
| 52 | +class AppServices: ServiceProvider { |
| 53 | + private lazy var onboardingScreenTask = OnboardingScreenTask() |
| 54 | + private lazy var homeScreenTask = HomeScreenTask() |
| 55 | + private lazy var pushNotoificationTask = PushNotoificationTask() |
| 56 | + |
| 57 | + lazy var appTasks: [AppTask] = { |
| 58 | + /// We need to present onboarding screens before showing the home screen, so we add a dependency from `homeScreenTask` to `onboardingScreenTask` this way, `homeScreenTask` won't be ran until `onboardingScreenTask` calls the `setFinished()` method. |
| 59 | + homeScreenTask.addDependency(onboardingScreenTask) |
| 60 | + return [ |
| 61 | + onboardingScreenTask, |
| 62 | + homeScreenTask, |
| 63 | + pushNotoificationTask |
| 64 | + ] |
| 65 | + }() |
| 66 | + |
| 67 | + /// `modules` method implementation is required by the `ServiceLoactor` protocol. |
| 68 | + func modules() -> [Register] { |
| 69 | + /// If we want to use `@Inject` to have the stored instance of a task injected to an other class, we can do so by registering it with the `ServiceLocator`. |
| 70 | + return [ |
| 71 | + Register { homeScreenTask } |
| 72 | + ] |
| 73 | + } |
| 74 | +} |
| 75 | +``` |
| 76 | + |
| 77 | +## AppTask |
| 78 | +### Description |
| 79 | +An `AppTask` is some kind of setup task that needs to be performed before any `AppDelegate` delegate call is performed. |
| 80 | + |
| 81 | +To create an `AppTask` you must provide an object that conforms to `AppTask` , overrides its `main()` method, and calls `setFinished()` when it finishes the process. |
| 82 | + |
| 83 | +### Example |
| 84 | + |
| 85 | +```swift |
| 86 | +/// Conform to `AppTask` |
| 87 | +class OnboardingScreenTask: AppTask { |
| 88 | + private let onboardingNavigator: OnboardingNavigator |
| 89 | + init(onboardingNavigator: OnboardingNavigator) { ... } |
| 90 | + |
| 91 | + /// Override `main()` |
| 92 | + override func main() { |
| 93 | + /// Presents onboarding screens |
| 94 | + onboardingNavigator.navigate(completion: { |
| 95 | + /// Call `setFinisehd()` when onboarding is completed. |
| 96 | + self.setFinished() |
| 97 | + }) |
| 98 | + } |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +### NoOpTask |
| 103 | + |
| 104 | +### Description |
| 105 | + |
| 106 | +A `NoOpTask` is a task that finishes immediately. The use of this kind of task is to receive and react to `AppDelegate` delegate calls. It will keep receiving these events throught the app’s lifecycle. |
| 107 | + |
| 108 | +### Example |
| 109 | + |
| 110 | +```swift |
| 111 | +/// Conform to `AppTask` |
| 112 | +class PushNotoificationTask: NoOpTask { |
| 113 | + private let pushService: PushService |
| 114 | + init(pushService: PushService) { ... } |
| 115 | + func application( |
| 116 | + _ application: UIApplication, |
| 117 | + didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { |
| 118 | + pushService.application( |
| 119 | + application, |
| 120 | + didRegisterForRemoteNotificationsWithDeviceToken: deviceToken |
| 121 | + ) |
| 122 | + } |
| 123 | +} |
| 124 | +``` |
0 commit comments