|
| 1 | +# Inject |
| 2 | +Hot reloading workflow helper that enables you to save hours of time each week, regardless if you are using `UIKit`, `AppKit` or `SwiftUI`. |
| 3 | + |
| 4 | +[Read detailed article about this](https://merowing.info/2022/03/hot-reloading-in-swift/) |
| 5 | + |
| 6 | +The heavy lifting is done by the amazing [InjectionForXcode](https://github.com/johnno1962/InjectionIII). This library is just a think wrapper to provide the best developer experience possible while requiring minimum effort. |
| 7 | + |
| 8 | +I've been using it for years. |
| 9 | + |
| 10 | +## What is hot reloading? |
| 11 | +Hot reloading is a technique allowing you to get rid of compiling your whole application and avoiding deploy/restart cycles as much as possible, all while allowing you to edit your running application code and see changes reflected as close as possible to real-time. |
| 12 | + |
| 13 | +This makes you significantly more productive by reducing the time you spend waiting for apps to rebuild, restart, re-navigate to the previous location where you were in the app itself, re-produce the data you need. |
| 14 | + |
| 15 | +This can save you literal hours off development time, **each day**! |
| 16 | + |
| 17 | +## Does it add manual overhead to my workflows? |
| 18 | +Once you configured your project initially, it's practically free. |
| 19 | + |
| 20 | +You don’t need to add conditional compilation or remove `Inject` code from your applications for production, it's already designed to behave as no-op inlined code that will get stripped by LLVM in non-debug builds. |
| 21 | + |
| 22 | +Which means that you can enable it once per view and keep using it for years to come. |
| 23 | + |
| 24 | +# Integration |
| 25 | +### Initial project setup |
| 26 | + |
| 27 | +To integrate `Inject` just add it as SPM dependency: |
| 28 | + |
| 29 | +### via Xcode |
| 30 | + |
| 31 | +Open your project, click on File → Swift Packages → Add Package Dependency…, enter the repository url (`https://github.com/krzysztofzablocki/Inject.git`) and add the package product to your app target. |
| 32 | + |
| 33 | +### via SPM package.swift |
| 34 | + |
| 35 | +```swift |
| 36 | +dependencies: [ |
| 37 | + .package( |
| 38 | + name: "Inject", |
| 39 | + url: "https://github.com/krzysztofzablocki/Inject.git", |
| 40 | + from: "1.0.0" |
| 41 | + ) |
| 42 | +] |
| 43 | +``` |
| 44 | +### Individual Developer setup (once per machine) |
| 45 | +If anyone in your project wants to use injection, they only need to: |
| 46 | + |
| 47 | +- You must add "-Xlinker -interposable" (without the double quotes) to your project's "Other Linker Flags" for the Debug target (qualified by the simulator SDK to avoid complications with bitcode), refer to [InjectionForXcode documentation](https://github.com/johnno1962/InjectionIII#limitationsfaq) if you run into any issues |
| 48 | +- Download newest version of Xcode Injection from it's [GitHub Page](https://github.com/johnno1962/InjectionIII/releases) |
| 49 | + - Unpack it and place under `/Applications` |
| 50 | +- Make sure that the Xcode version you are using to compile our projects is under the default location: `/Applications/Xcode.app` |
| 51 | +- Run the injection application |
| 52 | +- Select open project / open recent from it's menu and pick the right workspace file you are using |
| 53 | + |
| 54 | + After choosing the project in Injection app, launch the app |
| 55 | +- If everything is configured correctly you should see similar log in the console: |
| 56 | + |
| 57 | +```bash |
| 58 | +💉 InjectionIII connected /Users/merowing/work/SourceryPro/App.xcworkspace |
| 59 | +💉 Watching files under /Users/merowing/work/SourceryPro |
| 60 | +``` |
| 61 | + |
| 62 | +## Workflow integration |
| 63 | +You can either add `import Inject` in individual files in your project or use |
| 64 | +`@_exported import Inject` in your project target to have it automatically available in all its files. |
| 65 | + |
| 66 | +#### **SwiftUI** |
| 67 | +Just 2 steps to enable injection in your `SwiftUI` Views |
| 68 | + |
| 69 | +- Add `@ObservedObject private var iO = Inject.observer` variable |
| 70 | +- call `.enableInjection()` at the end of your body definition |
| 71 | + |
| 72 | +> *Remember you **don't need** to remove this code when you are done, it's NO-OP in production builds.* |
| 73 | +
|
| 74 | +#### **UIKit / AppKit** |
| 75 | +For standard imperative UI frameworks we need a way to clean-up state between code injection phases. |
| 76 | + |
| 77 | +I create the concept of **Hosts** that work really well in that context, there are 2: |
| 78 | + |
| 79 | +- `Inject.ViewControllerHost` |
| 80 | +- `Inject.ViewHost` |
| 81 | + |
| 82 | +How do we integrate this? We wrap the class we want to iterate on at the parent level, so we don’t modify the class we want to be injecting but we modify the parent callsite. |
| 83 | + |
| 84 | +Eg. If you have a `SplitViewController` that creates `PaneA` and `PaneB `, and you want to iterate on layout/logic code in `PaneA`, you modify the callsite in `SplitViewController`: |
| 85 | + |
| 86 | +```swift |
| 87 | +paneA = Inject.ViewHost( |
| 88 | + PaneAView(whatever: arguments, you: want) |
| 89 | +) |
| 90 | +``` |
| 91 | + |
| 92 | +That is all the changes you need to do, your app now allows you to change anything in `PaneAView` except for its initialiser API and the changes will be almost immediately reflected in your App. |
| 93 | + |
| 94 | +> Host changes can’t be fully inlined, so those classes are removed in release builds, to accommodate for that inconvenience the easiest way is to simply make a separate commit that swaps this one-liner and then remove it at the end of your workflow. |
| 95 | +
|
| 96 | +#### The Composable Architecture |
| 97 | + |
| 98 | +If like myself you love [PointFree](https://pointfree.co/) Composable Architecture, you’d probably want to inject reducer code, this isn’t possible in vanilla TCA because reducer code is a free function which isn’t as straightforward to replace with injection, but [our fork](https://github.com/thebrowsercompany/swift-composable-architecture) at [The Browser Company](https://thebrowser.company/) supports it. |
0 commit comments