{"id":3209,"url":"https://github.com/johnno1962/injectionforxcode","last_synced_at":"2025-05-14T11:08:40.741Z","repository":{"id":3420768,"uuid":"4471940","full_name":"johnno1962/injectionforxcode","owner":"johnno1962","description":"Runtime Code Injection for Objective-C \u0026 Swift","archived":false,"fork":false,"pushed_at":"2022-08-04T00:53:23.000Z","size":6639,"stargazers_count":6555,"open_issues_count":77,"forks_count":565,"subscribers_count":152,"default_branch":"master","last_synced_at":"2025-04-11T04:17:47.016Z","etag":null,"topics":["xcode-plugin","xcodebuild"],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/johnno1962.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-05-28T15:37:09.000Z","updated_at":"2025-03-27T21:27:10.000Z","dependencies_parsed_at":"2022-07-14T04:00:33.984Z","dependency_job_id":null,"html_url":"https://github.com/johnno1962/injectionforxcode","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnno1962%2Finjectionforxcode","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnno1962%2Finjectionforxcode/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnno1962%2Finjectionforxcode/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnno1962%2Finjectionforxcode/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/johnno1962","download_url":"https://codeload.github.com/johnno1962/injectionforxcode/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254129481,"owners_count":22019628,"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":["xcode-plugin","xcodebuild"],"created_at":"2024-01-05T20:16:34.537Z","updated_at":"2025-05-14T11:08:40.682Z","avatar_url":"https://github.com/johnno1962.png","language":"Objective-C","readme":"# ![Icon](http://injectionforxcode.johnholdsworth.com/injection.png) Injection Plugin for Xcode\n\nCopyright (c) John Holdsworth 2012-19\n\n# TLDR:\nXcode is an integrated development environment (IDE) for macOS containing a suite of software development tools developed by Apple for developing software for macOS, iOS, iPadOS, watchOS, and tvOS.\nInjection for Xcode is an Xcode plugin (available via [Alcatraz](http://alcatraz.io/)) or [AppCode](#user-content-use-with-appcode) that \ndynamically inserts new Swift / Objective-C code into a running app in order to speed up your build process. It does this without making _any_ changes to your project.\n\nAn up-to-date overview by Rob Norback of [how to incorporate it into your workflow is here](https://medium.com/@robnorback/the-secret-to-1-second-compile-times-in-xcode-9de4ec8345a1)\n\n*This repo is no longer maintained*. The current version of Injection is [InjectionIII](https://github.com/johnno1962/InjectionIII) in the Mac App Store.\n\n![Injection Example](documentation/images/injected.gif)\n\nAnnouncements of major additions to the project will be made on twitter [@Injection4Xcode](https://twitter.com/@Injection4Xcode).\n\n### Stop Press\n\nIf you know the trick which I won't detail here plugins still load in Xcode 8 GM and injection has been\nupdated for it and Swift 3. Patched injection works fine and unpatched injection works if the\n\"InjectionLoader\" bundle is codesigned for the simulator. This is done in a \"Run Script\" build phase in this\nproject if you need to update the code signing identity to dis-ambiguate it. Please raise issues \nwith any problems or look at [this blog](https://johntmcintosh.com/blog/2016/10/03/code-injection-ios).\nOne thing I have noticed is you can no longer add methods using injection which was proably never a\nparticularly good idea in Swift.\n\n### Stop Stop Press\n\nInjection is now available as a standalone app rather than have to build the plugin which you can download [here](http://johnholdsworth.com/injection.html). As injection no longer works on the deivce due to sandboxing in iOS10 this is the recommended route going forward. For more information consult the [FAQ](https://johnno1962.github.io/InjectionApp/injectionfaq.html).\n\nFor TDD, there is a interesting fork of the injection plugin you can download [here](https://github.com/polac24/injectionforxcode/tree/tdd). It runs all tests covering a source file each time you inject. It's on the 'tdd' branch.\n\n## How to Use Injection for Xcode\n\nFor installation and usage for AppCode [see below](#user-content-use-with-appcode). If you're a visual learner, you may appreciate [this video post](http://artsy.github.io/blog/2016/03/05/iOS-Code-Injection/) from [@Orta](https://twitter.com/@orta) covering the basics.\n\nWith Xcode, either install via Alcatraz, or install by cloning this repo and build `InjectionPluginLite/InjectionPlugin.xcodeproj`. If you are building locally, note that you need to restart Xcode to load the plugin. A popup should appear asking to confirm loading a plugin not signed by Apple, that signals that the plugin is set up.\n\nThe plugin can be removed either via Alcatraz, or by running: `rm -rf ~/Library/Application\\ Support/Developer/Shared/Xcode/Plug-ins/InjectionPlugin.xcplugin`\n\n### Simple Proof of Concept Once Installed\n\nOnce it is installed, compile and run a project as normal. From here you should take any class that would exist when your \napp is loaded, add a new function `- injected`  and add a breakpoint on that line.\n\n``` objc\n- (void)injected\n{\n    NSLog(@\"I've been injected: %@\", self);\n}\n```\nor\n``` swift\n@objc func injected() {\n    print(\"I've been injected: \\(self)\")\n}\n```\n\nThen press \u003ckbd\u003ectrl\u003c/kbd\u003e+\u003ckbd\u003e=\u003c/kbd\u003e, and you'll see Xcode stop at the breakpoint. You've just injected new code into a running app. **Awesome right?**\n\n### Callbacks in Your Code\n\nYou can build on top of Injection from three callbacks:\n\n* `- (void)injected` as an instance method, which gives you the chance to re-update an object with new contexts.\n* `+ (void)injected` as a class method, making it possible to update globals with new information\n* Listening for `INJECTION_BUNDLE_NOTIFICATION`, allowing other classes to listen out for injection calls, this is useful for providing [app level changes](https://github.com/artsy/eigen/pull/1236).\n\nIf you are interested in diving even deeper into callbacks, check out [Tunable Parameters](documentation/tunable_parameters.md).\n\n![Tunable Example](documentation/images/injection_tunable.gif)\n\n## Swift Support\n\nSwift support works great when working with a collection of classes. \nHowever, there are a number of cases where re-injection won't work elegantly with Swift:\n\n * Making changes to Structs.\n * Changing `func` or `class`es that are marked as `final`.\n * Global `func` or variables that are not constrained into a class.\n * Compiler optimisations can stop injection working. If so, build DEBUG.\n \nIn day-to-day development against Cocoa/UIKit, it's rare, but possible to hit these cases, \nfor more information see [What Happens With Swift?](#what-happens-with-swift)\n\n## How it Works\n\nInjection for Xcode is an extension to the Xcode IDE that allows you to patch the implementation\nof a class's method without having to restart the application. \n\nIt performs this by parsing the build logs of the application to determine how a source file was \nlast compiled. With this it wraps the result of re-compiling into a bundle which is injected into \nthe application using the dynamic loader. At this point there are two versions of a class in the app,\nthe original and a new modified version from the bundle. The modified version is then \"swizzled\"\nonto the original class so changes take effect.\n\nThis swizzling takes advantage of the fact that Objective-C binds method invocations to\nimplementations at run time. This can also be performed on Swift classes provided that\nthe method or class is not final or private (i.e. the method can be overridden) by\npatching the class' \"vtable\". This excludes the injection of methods of structs.\n\n## What Else Does This Plugin Do?\n\n* It has file watcher that can inject when a file is saved from whichever editor you use. Enable this in the preferences pane accessed on menu item \"Product/Injection Plugin/Tunable Parameters\": \"File Watcher\".\n\n* There is support for working specifically with [Storyboard-based iOS projects](documentation/storyboards.md).\n\n* The plugin offers a way to quickly change a [collection of tunable parameters](documentation/tunable_parameters.md)\n\n* Xcode is given a badge, showing the number of active Injection connections to apps.\n\n* When you start using Injection, a new Xcode Project is added to the same folder as your\nproject (either `iOSInjectionProject` or `OSXInjectionProject`.) This is the xcode project base\nfor the changes which are injected into your project, it is recommended to add this to your `.gitignore`.\n\n* Each time a project is injected, `injectionCount.txt` in the injection project's directory (above) is incremented. This can give you a sense of how much time you save (number of injections * amount saved per injection = a happier you).\n\n* The injection key command can be customised from \u003ckbd\u003ectrl\u003c/kbd\u003e+\u003ckbd\u003e=\u003c/kbd\u003e in the \"Tunable App Parameters\" panel.\n\n* Works on a device, if you apply a [patch to your project.](documentation/patching_injection.md).\n\n* Perform unit tests that cover your Swift class/struct/enum. For details see [TDD with Injection for Xcode](documentation/tdd.md)\n\n## What Happens with Swift?\n\n![Icon](http://injectionforxcode.johnholdsworth.com/injection2.png)\n\nSwift, presents a few more stumbling blocks for the uninitiated. Provided that methods are of\na non final class and are non final (this excludes structs alas) they can be injected.\nIn this example the `sharedInstance` variable is declared `static` rather than \"class\" to make\nsure it is not injected to ensure there is only ever one singleton. For the \"injected\"\nmethods to work your class _must_ inherit from NSObject.\n\nMore problematic is the more common use of variables or functions outside a class which are\nreferred to across the files of a bundle. Swift 1.2+ takes the view these \"internal\" scope\nsymbols should not be available across bundles and are made \"private extern\" in\ntheir object file making them unavailable at run time. This means that the above code\nwill inject but injecting another file referring to the dispatch_on_main function will fail\nwith obscure dynamic loading errors.\n\nThe simplest solution is to make these variables and functions public though, for a framework,\nthis may be unsatisfactory. The alternative is to patch the object files of the project to remove the\nprivate extern flag and relink the bundle. In order to do this a script `~/bin/unhide.sh`\nis created by the plugin build which should be called as an additional \"Run Script\"\nbuild phase after linking your app to perform this patch and relink. \n\n## Use with AppCode \n\nInjection can be used from inside AppCode provided the application [has been patched](documentation/patching_injection.md) and\nyou have previously injected that project from inside Xcode to set up a link to the \nbuild logs. \n\nTo install, download the jar file `InjectionPluginAppCode/Injection.jar` from this repo \nand go to AppCode preferences, choose \"Install plugin from disk\" and locate the .jar.\nThen restart the IDE. Now you will get new menu options under the Run menu.\nYou’ll need to re-patch the project from inside AppCode as it uses a different port number to connect.\n\n## Limitations of Injection\n\nThere are limitations of course, largely centering around static variables, static or global\nfunctions and their Swift equivalents. Consider the following Objective-C code.\n\n![Icon](http://injectionforxcode.johnholdsworth.com/injection1.png)\n\n* One potential problem is when the new version of the class is loaded, it comes with it's own\nversions of static variables such as `sharedInstance` and the `once` token.  After injection \nhas occurred, this would generate a new singleton instance. \n\n  To prevent this, class methods with the prefix \"shared\" are not swizzled on injection to \nsupport this common idiom.\n\n* It can be tough to look through all of the memory of a running application. In order to determine the classes and instances to call the `injected` callbacks on, Injection performs a \"sweep\" to find all objects in memory. Roughly, this involves looking at an object, then recursively looking through objects which it refers to. For example, the object's instance variables and properties.\n\n  This process is seeded using the application's delegate and all windows. Once all the in-memory reference are collected, Injection will then filter these references to ones that it has compiled and injected. Then sending them the messages referenced in the [callbacks section](##user-content-callbacks-in-your-code).\n  \n  If no references are found, Injection will look through all objects that are referred to via `sharedInstance`. If that fails, well, Injection couldn't find your instance. This is one way in which\n  you may miss callbacks in your app.\n\n* The function `dispatch_on_main` does not inject, as it has been statically linked into\nthe application. It does however, inject by proxy in the case shown via the `doSomething`\nmethod. `dispatch_on_main` will have been linked locally to a version in the object file being injected.\n\n","funding_links":[],"categories":["\u003ca id=\"977cef2fc942ac125fa395254ab70eea\"\u003e\u003c/a\u003eXCode","HarmonyOS","Injection","Objective-C","Mobile","Objective-C  Stars 1000以内排名整理","WebSocket","Code Injection"],"sub_categories":["\u003ca id=\"7037d96c1017978276cb920f65be2297\"\u003e\u003c/a\u003e工具","Windows Manager","Other free courses","Web View"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnno1962%2Finjectionforxcode","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohnno1962%2Finjectionforxcode","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnno1962%2Finjectionforxcode/lists"}