• Stars
    star
    2,127
  • Rank 21,695 (Top 0.5 %)
  • Language
    Swift
  • License
    MIT License
  • Created about 3 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Hot Reloading for Swift applications!

Inject

Hot reloading workflow helper that enables you to save hours of time each week, regardless if you are using UIKit, AppKit or SwiftUI.

If you'd like to support my work and improve your engineering workflows, check out my SwiftyStack course

TLDR: A single line of code change allows you to live code UIKit screen:

HotReloadingDemo.mp4

Read detailed article about this

The heavy lifting is done by the amazing InjectionIII. This library is just a thin wrapper to provide the best developer experience possible while requiring minimum effort.

I've been using it for years.

What is hot reloading?

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.

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.

This can save you literal hours off development time, each day!

Does it add manual overhead to my workflows?

Once you configured your project initially, it's practically free.

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.

Which means that you can enable it once per view and keep using it for years to come.

Integration

Initial project setup

To integrate Inject just add it as SPM dependency:

via Xcode

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.

via SPM package.swift

dependencies: [
    .package(
      url: "https://github.com/krzysztofzablocki/Inject.git",
      from: "1.2.4"
    )
]

via Cocoapods Podfile

pod 'InjectHotReload'

Individual Developer setup (once per machine)

If anyone in your project wants to use injection, they only need to:

  • You must add "-Xlinker -interposable" (without the double quotes and on separate lines) to the "Other Linker Flags" of all targets in your project for the Debug configuration (qualified by the simulator SDK to avoid complications with bitcode), refer to InjectionForXcode documentation if you run into any issues
  • Download newest version of Xcode Injection from it's GitHub Page
  • Unpack it and place under /Applications
  • Make sure that the Xcode version you are using to compile our projects is under the default location: /Applications/Xcode.app
  • Run the injection application
  • Select open project / open recent from it's menu and pick the right workspace file you are using

After choosing the project in Injection app, launch the app

  • If everything is configured correctly you should see similar log in the console:
💉 InjectionIII connected /Users/merowing/work/SourceryPro/App.xcworkspace
💉 Watching files under /Users/merowing/work/SourceryPro

Workflow integration

You can either add import Inject in individual files in your project or use @_exported import Inject in your project target to have it automatically available in all its files.

SwiftUI

Just 2 steps to enable injection in your SwiftUI Views

  • call .enableInjection() at the end of your body definition
  • add @ObserveInjection var inject to your view struct

Remember you don't need to remove this code when you are done, it's NO-OP in production builds.

If you want to see your changes in action, you can enable an optional Animation variable on InjectConfiguration.animation that will be used when ever new source code is injected into your application.

InjectConfiguration.animation = .interactiveSpring()

Using Inject is demoed in this example app

UIKit / AppKit

For standard imperative UI frameworks we need a way to clean-up state between code injection phases.

I create the concept of Hosts that work really well in that context, there are 2:

  • ViewControllerHost
  • ViewHost

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.

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:

paneA = Inject.ViewHost(
  PaneAView(whatever: arguments, you: want)
)

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.

Make sure to call initializer inside Inject.ViewControllerHost(...) or Inject.ViewHost(...). Inject relies on @autoclosure to reload views when hot-reload happens. Example:

// WRONG
let viewController = YourViewController()
rootViewController.pushViewController(Inject.ViewControllerHost(viewController), animated: true)

// CORRECT
let viewController = Inject.ViewControllerHost(YourViewController())
rootViewController.pushViewController(viewController, animated: true)

Remember you don't need to remove this code when you are done, it's NO-OP in production builds.

Injection Hook for UIKit

depending on the architecture used in your UIKit App, you might want to attach a hook to be executed each time a view controller is reloaded.

Eg. you might want to bind the UIViewController to the presenter each-time there's a reload, to achieve this you can use onInjectionHook Example:

myView.onInjectionHook = { hostedViewController in
//any thing here will be executed each time the controller is reloaded
// for example, you might want to re-assign the controller to your presenter
presenter.ui = hostedViewController
}

iOS 12

You need to add -weak_framework SwiftUI to Other Linker Flags for iOS 12 to work.

The Composable Architecture

If like myself you love PointFree 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 at The Browser Company supports it.

More Repositories

1

Sourcery

Meta-programming for Swift, stop writing boilerplate code.
Swift
7,677
star
2

LifetimeTracker

Find retain cycles / memory leaks sooner.
Swift
3,144
star
3

Playgrounds

Better playgrounds that work both for Objective-C and Swift
Objective-C
2,633
star
4

Swift-Macros

A curated list of awesome Swift Macros
Swift
2,120
star
5

Bootstrap

iOS project bootstrap aimed at high quality coding.
Objective-C
2,047
star
6

LineDrawing

Beatiful and fast smooth line drawing algorithm for iOS - as seen in Foldify.
Objective-C
1,288
star
7

Difference

Simple way to identify what is different between 2 instances of any type. Must have for TDD.
Swift
1,217
star
8

PropertyMapper

Property mapping for Objective-C iOS apps.
Objective-C
1,124
star
9

KZFileWatchers

A micro-framework for observing file changes, both local and remote. Helpful in building developer tools.
Swift
1,078
star
10

LinkedConsole

Clickable links in your Xcode console, so you never wonder which class logged the message.
Swift
931
star
11

Traits

Modify your native iOS app in real time.
Swift
904
star
12

IconOverlaying

Build informations on top of your app icon.
Shell
650
star
13

crafter

Crafter - Xcode project configuration CLI made easy.
Ruby
547
star
14

Strongify

Strongify is a 1-file µframework providing a nicer API for avoiding weak-strong dance.
Swift
444
star
15

KZNodes

Have you ever wonder how you could make Origami like editor in 1h ?
Objective-C
336
star
16

SFObservers

NSNotificationCenter and KVO auto removal of observers.
Objective-C
307
star
17

AutomaticSettings

Data driven settings UI generation.
Swift
302
star
18

CCNode-SFGestureRecognizers

Adding UIGestureRecognizers to cocos2d, painless.
Objective-C
202
star
19

DetailsMatter

Objective-C
199
star
20

Pinch-to-reveal

Pinch to reveal animation transition built with Layer masking, as seen in boeing app for iPad.
Objective-C
192
star
21

ViewModelOwners

Protocols that help make your MVVM setup more consistent
Swift
143
star
22

KZAsserts

Asserts on roids, test all your assumptions with ease.
Objective-C
100
star
23

OhSnap

Reproduce bugs your user saw by capturing and replaying data snapshots with ease.
Swift
89
star
24

Versionable

Migration for `Codable` objects.
Swift
83
star
25

SFContainerViewController

UIViewControllers containment predating Apple implementation. Works in both 4.x and 5.x iOS, no memory or hierarchy issues.
Objective-C
82
star
26

Swift-Observable

Native KVO like behaviour build in Swift.
Swift
64
star
27

BehavioursExample

Objective-C
58
star
28

SourceryWorkshops

Swift
45
star
29

Learn-iOS-GameDev-Level-0

Teeter clone accompanying tutorial at http://merowing.info/2013/04/learn-ios-game-dev-level-0/
Objective-C
24
star
30

XibReferencing

Simple category and sample showing how you can reference one Xib view from another
Objective-C
20
star
31

KZImageSplitView

Objective-C
17
star
32

NSObject-SFExecuteOnDealloc

A simple category on NSObject that allows you to execute block when object is deallocated
Objective-C
17
star
33

jenkins_jobs_to_statusboard

Ruby script that generates html table for embedding in StatusBoard by Panic http://panic.com/statusboard/
Ruby
15
star
34

SourceryPro-Feedback

Repository for discussing https://merowing.info/sourcery-pro/
12
star
35

krzysztofzablocki

2
star
36

krzysztofzablocki.github.io

Blog
HTML
2
star
37

starter-hugo-academic

Jupyter Notebook
1
star