{"id":3210,"url":"https://github.com/zenangst/Vaccine","last_synced_at":"2025-08-06T16:32:14.898Z","repository":{"id":56925857,"uuid":"134853686","full_name":"zenangst/Vaccine","owner":"zenangst","description":":syringe: Vaccine - Make your apps immune to recompile-disease","archived":false,"fork":false,"pushed_at":"2020-01-10T14:50:27.000Z","size":222,"stargazers_count":307,"open_issues_count":2,"forks_count":18,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-12-08T09:02:31.990Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zenangst.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-05-25T12:31:39.000Z","updated_at":"2024-10-30T00:09:44.000Z","dependencies_parsed_at":"2022-08-21T06:20:57.175Z","dependency_job_id":null,"html_url":"https://github.com/zenangst/Vaccine","commit_stats":null,"previous_names":[],"tags_count":41,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zenangst%2FVaccine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zenangst%2FVaccine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zenangst%2FVaccine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zenangst%2FVaccine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zenangst","download_url":"https://codeload.github.com/zenangst/Vaccine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228923758,"owners_count":17992574,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-01-05T20:16:34.571Z","updated_at":"2024-12-09T16:31:20.398Z","avatar_url":"https://github.com/zenangst.png","language":"Swift","funding_links":[],"categories":["Libs","Swift","Code Injection","HarmonyOS","Utilities and Extensions","Utility [🔝](#readme)"],"sub_categories":["Utility","Web View","Windows Manager"],"readme":"# Vaccine\n\n[![CI Status](https://travis-ci.org/zenangst/Vaccine.svg?branch=master)](https://travis-ci.org/zenangst/Vaccine)\n[![Version](https://img.shields.io/cocoapods/v/Vaccine.svg?style=flat)](http://cocoadocs.org/docsets/Vaccine)\n[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![License](https://img.shields.io/cocoapods/l/Vaccine.svg?style=flat)](http://cocoadocs.org/docsets/Vaccine)\n[![Platform](https://img.shields.io/cocoapods/p/Vaccine.svg?style=flat)](http://cocoadocs.org/docsets/Vaccine)\n![Swift](https://img.shields.io/badge/%20in-swift%204.0-orange.svg)\n\n## Description\n\n\u003cimg src=\"https://github.com/zenangst/Vaccine/blob/master/Images/Vaccine.png?raw=true\" alt=\"Vaccine Icon\" align=\"right\" /\u003e\n\nVaccine is a framework that aims to make your apps immune to recompile-disease. Vaccine provides a straightforward way to make your application ready for code injection, also known as hot reloading. It provides extensions on application delegates, NSObject and view controllers.\n\nBefore you go any further, make sure that you have InjectionIII installed and have understood the core concept for code injection and its limitations. \nFor more information about InjectionIII, head over to  [https://github.com/johnno1962/InjectionIII](https://github.com/johnno1962/InjectionIII).\n\nVaccine does not cut-out the need to ever recompile, but it opens up for faster iteration and seeing your application change instantly. There will be scenarios where you will have to recompile your application to see the changes appear. Worth noting is that code injection only works in the simulator and has no effect when running it on a device.\n\nFor additional information about how you can incorporate injection into your workflow, check out these articles:\n\n- [How To Be The iOS Team Hero: Add Hot Reloading to Xcode 9](https://medium.com/@robnorback/the-secret-to-1-second-compile-times-in-xcode-9de4ec8345a1)\n- [Code Injection In Swift](https://medium.com/itch-design-no/code-injection-in-swift-c49be095414c)\n\n## Usage\n\nThe following examples are not meant to be best practices or the defacto way of doing code injection. The examples are based on personal experiences when working on projects that use InjectionIII.\n\n### Example project\n\nThe easiest way to try Vaccine with InjectionIII is to run the example project.   \nFollow the steps below:\n\n1. Install InjectionIII from the [Mac App Store](https://itunes.apple.com/no/app/injectioniii/id1380446739?mt=12)\n2. `git clone git@github.com:zenangst/Vaccine.git`\n3. Run `pod install` in `Example/VaccineDemo/`\n4. Open and run `VaccineDemo.xcworkspace`\n5. Select the demo project when `InjectionIII` wants you to select a folder.\n6. Start having fun 🤩\n\n### General tips\n\nTo get the most bang for the buck, your view controllers should be implemented with dependency injection, that way you can provide dummy material that is relevant to your current context. It works well when you want to try out different states of your user interface.\n\n### Loading the injection bundle\n\nFor InjectionIII to work, you need to load the bundle located inside the application bundle. You want to do this as early as possible, preferably as soon as your application is done launching.\n\n```swift\n// Loads the injection bundle and registers \n// for injection notifications using `injected` selector.\nInjection.load(then: applicationDidLoad)\n         .add(observer: self, with: #selector(injected(_:)))\n```\n\n### Application delegate\n\nTo get the most out of code injection, you need be able to provide your application with a new instance of the class that you are injecting. A good point of entry for injecting code is to reinitialize your app at the application delegate level. It that increases the likely-hood of getting the desired effect of code injection as your root objects are recreated using the newly injected code. It also provides with a point of entry for displaying the target view controller(s) that you are modifying. So what it means in practice is that you can push or present the relevant view controller directly from your application delegate cutting out the need to manually recreating the view controller stack by manually navigating to the view controller you are editing. Working with InjectionIII is very similar to how playground-driven works, without having to wait for the playground to load or recompile your app as a framework.\n\n```swift\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n  var window: UIWindow?\n\n  func application(_ application: UIApplication,\n                   didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -\u003e Bool {\n                   Injection.load(then: applicationDidLoad).add(observer: self,\n                                                with: #selector(injected(_:)))\n    return true\n  }\n\n  @objc open func injected(_ notification: Notification) {\n    applicationDidLoad()\n    // Add your view hierarchy creation here.\n  }\n\n  private func applicationDidLoad() {\n    let window = UIWindow(frame: UIScreen.main.bounds)\n    window.rootViewController = ViewController()\n    window.makeKeyAndVisible()\n    self.window = window\n  }\n}\n```\n\nWhen the code gets injected, `applicationDidLoad` will be invoked. It cleans and recreates the entire view hierarchy by creating a new window.\n\n### View controllers\n\nInjecting view controllers is really where InjectionIII shines the most. Vaccine provides extensions to make this easy to setup and maintain. When injection notifications come in, Vaccine will filter out view controllers that do not fill the criteria for being reloaded. It checks if the current view controller belongs to a child view controller, if that turns out to be true, then it will reload the parent view controller to make sure that all necessary controllers are notified about the change.\n\n**Note**\nVaccine also supports adding injection using swizzling on views, view controllers and table and collection view data sources.\nThis features is enabled by default but can be disabled by setting `swizzling` to `false` when loading the bundle.\n\n```swift\nInjection.load(then: ..., swizzling: false)\n```\n\nWhen injecting a view controller, the following things will happen:\n\n- Removes the current injection observer\n- Remove views and layers\n- Invokes `viewDidLoad` to correctly set up your view controller again\n- Invokes layout related methods on all available subviews of the controller's view.\n- Invoke `sizeToFit` on all views that haven't received a size\n\nWhat you need to do in your view controllers is to listen to the incoming notifications and deregister when it is time to deallocate. Registering should be done in `viewDidLoad` as the observer will temporarily be removed during injection.\n\n```swift\nclass ViewController: UIViewController {\n  override func viewDidLoad() {\n    super.viewDidLoad()\n    Injection.add(observer: self, with: #selector(injected(_:)))\n    // Implementation goes here.\n  }\n}\n```\n\nWhen a view controller gets injected, it will invoke everything inside viewDidLoad, so any changes that you make to the controller should be rendered on screen.\n\n## Views\n\nInjection views are similar to view controllers, except that they don't have a conventional method that you override to build your custom implementation. Usually, you do everything inside the initializer. To make your view injection friendly, you should move the implementation from the initializer into a separate method that you can call whenever that view's class is injected.\n\n```swift\nclass CustomView: UIView {\n  override init(frame: CGRect) {\n    super.init(frame: frame)\n    addInjection(with: #selector(injected(_:)))\n    loadView()\n  }\n\n  required init?(coder aDecoder: NSCoder) {\n    fatalError(\"init(coder:) has not been implemented\")\n  }\n\n  private func loadView() {\n    // Your code goes here.\n  }\n\n  @objc open func injected(_ notification: Notification) {\n    loadView()\n  }\n}\n```\n\nIf you enable swizzling when loading the injection bundle, then the initializer for all views will be switch out in\norder to evaluate if your view conforms to injection. It does this by checking if the view responds to the `loadView` selector.\nThis removes the need to manual add injection related code into your views. Note that `loadView` needs `@objc` in order\nfor injection to properly find and invoke the method when the view gets injected. Views that do not respond to the\nselector will be ignored.\n\n```swift\nclass CustomView: UIView {\n  override init(frame: CGRect) {\n    super.init(frame: frame)\n    loadView()\n  }\n  \n  required init?(coder aDecoder: NSCoder) {\n    fatalError(\"init(coder:) has not been implemented\")\n  }\n\n  @objc private func loadView() {\n    // Your code goes here.\n  }\n}\n```\n\nIf you feel like this is a lot of code to write for all views that you create, I recommend\ncreating an Xcode template for creating views.\n\n## Auto layout constraints\n\nAdding additional constraints can quickly land you in a situation where your layout constraints are ambiguous. \nOne way to tackle this issue is to gather all your views constraints into an array, and at the top of your setup method, \nyou simply set these constraints to be deactivated. That way you can add additional constraints by continuing to inject, \nand the latest pair are the only ones that will be active and in use.\n\nWhen using `swizzling`, the framework will try and resolve the `layoutConstraints` from your view and deactivate them in order to avoid\nconflict with any new constraints that you may apply in your `loadView()` method. Which means that you can remove the call to `NSLayoutConstraint` to deactivate the current constraints. \n\n**Note**: Using `layoutConstraints` is optional, if your view does not use stored constraints, then Vaccine will recursively deactivate all constraints on all of its subviews when the view gets injected.\n\n```swift\nclass CustomView: UIView {\n  private var layoutConstraints = [NSLayoutConstraint]()\n  \n  private func loadView() {\n    NSLayoutConstraint.deactivate(layoutConstraints)\n    // Your code goes here.\n  }\n}\n```\n\n\n## Installation\n\n**Vaccine** is available through [CocoaPods](http://cocoapods.org). To install\nit, simply add the following line to your Podfile:\n\n```ruby\npod 'Vaccine'\n```\n\n**Vaccine** is also available through [Carthage](https://github.com/Carthage/Carthage).\nTo install just write into your Cartfile:\n\n```ruby\ngithub \"zenangst/Vaccine\"\n```\n\n**Vaccine** can also be installed manually. Just download and drop `Sources` folders in your project.\n\n## Author\n\nChristoffer Winterkvist, christoffer@winterkvist.com\n\n## Credits\n\n- [Vadym Markov](https://github.com/vadymmarkov) for giving inspiration to the swizzling feature. [[Source]](https://github.com/vadymmarkov/Fashion/blob/master/Sources/Shared/Swizzler.swift)   \n- [John Holdsworth](https://github.com/johnno1962/InjectionIII) for making runtime code injection possible.\n\n## Contributing\n\nWe would love you to contribute to **Vaccine**, check the [CONTRIBUTING](https://github.com/zenangst/Vaccine/blob/master/CONTRIBUTING.md) file for more info.\n\n## License\n\n**Vaccine** is available under the MIT license. See the [LICENSE](https://github.com/zenangst/Vaccine/blob/master/LICENSE.md) file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzenangst%2FVaccine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzenangst%2FVaccine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzenangst%2FVaccine/lists"}