Injection Overview

Injection allows you to inject code changes into an application while it is running for development and debugging. It is intended as an alternative to the "Fix and Continue" functionality which was removed in Xcode® 4. By using a set of simple preprocessor macros, it allows your classes to be recompiled selectively as class categories which can be loaded at runtime by placing them in a bundle and having your application load it. The Objective-C runtime will always use the category version of method implementations in preference to the version the application was originally compiled with so your changes take effect. This process is completely automated by the Injection application which runs along side Xcode looking for source code changes and preparing a bundle and loading it using a socket connection. The process can be repeated indefinitely, works for OS X and iOS applications and on iOS devices when the bundle is code-signed as it is copied onto the device.

The first time it runs, Injection will have opened two example projects showing the fundamentals of how a class is prepared for injection. Press the "Open App Project" button for the InjectionDemo application to build and run it. Injection shows a set of sliders which can be used to affect the appearance of the "rose" displayed in the simulator by updating parameters available in a set of global variables in real time. You can also make changes to the "InRoseView.m" implementation, save them and see them take effect immediately in the running application. This gives an idea of how injection works.

Injection has now been integrated into Xcode as a Plugin and no longer needs to patch your code as described below. For details, please see this site.

Using Injection

The work flow to make one of your own projects "injectable" is as follows: Open the application project using the "File/Open Project" menu item. This will make minor changes to the program's main.m and <Project>-Prefix.pch to have the program connect to the Injection application when it runs and make available a set of preprocessor macros to all sources which are used to convert your classes to categories. More about this later. It also creates an Xcode bundle sub-project which can be used to build a loadable bundle of any classes which have changed.

When a project is open, the "Build" button includes an indicator showing the following status colours:

Client application is not connected to Injection.
Client application connected waiting for code changes.
A script is running to open the project or build and load bundle.
A bundle compilation or load error has occurred.
Automatic build is turned off and source files have changed.

Once the project is open in Injection press the "Control Panels" button twice to view a list of implementation files in the project and click on the "convert" link for the files you wish to work on. Run the application and it should connect to Injection changing the "Build" button indicator to green. You should then be able save any of the files converted and have them load into the running application. If the file is not writable you will see an "unlock" link to perform the conversion. You can customise the command used for this (e.g. to check out of source control) in the Injection application's preferences.

If the bundle is unable to build or load it is likely to be a linkage error which you can resolve by opening the bundle subproject and adding any additional frameworks or header include paths until it builds. As an alternative to get started you can use one of Apple's example projects to get up and running by following the steps in the "Quick-start guide" at the end of this document.

How it works

The basic trick to allow classes to load at run time is to have them recompile as categories for loading into a bundle. In order to do this the Injection application modifies the class' @implementation statement from:

@implementation INRoseView

is converted to:

@implementation _injectable(INRoseView)

_injectable() is one of the pre-processor macros included in the project's <Project>-Prefix.pch header which is normally just a passthrough but when the class is recompiled for use in a bundle converts the declaration into that of a category with an incrementing category name. Some other changes are required as well, for example global variables need to be converted into "extern" statements in a bundle with any initialisers removed. In order to do this the statement:

NSString *aGlobalVariable = @"initialiser";

is converted to:

_inglobal NSString *aGlobalVariable _inval( @"initialiser" );

The _inglobal macro expands to an "extern" when the class is compiled as a category and to avoid warnings _inval() becomes a no-op. "static" variables are converted into globals to not have the class loose it's state and treated in the same way as globals. If a static variable is not seen to be messaged or assigned to it will become an _inprivate variable which will be an actual static.

The full list of macros used and their declarations for the main projects compile and when they are compiled for use in a bundle is below.

Pre-Processor MacroBuilding ProjectDefinition while building bundle
_injectable(_className)_className_className(INJECTION_BUNDLE)
_injectable_category(_className,_category)_className(_category)_className(INJECTION_BUNDLE##_##_category)
_inglobal

/* */

extern

_instatic

/* */

extern

_inprivate

static

static

_inval(_value...)= _value

/* _value */

One final change Injection performs while converting a class is to parse any @properties declarations it finds in the class and any "extensions" to explicitly declare these as ivars the old fashioned way in the class header if they are not already defined. This is required if any reference it made to the ivars directly in the source rather than through the self.ivar syntax. This is unfortunate but a necessary evil for a category to be able link against these ivars. The ivar declaration is conditional so the application will not be released in this configuration in fact all of injection can only take effect while the DEBUG symbol is declared and the INJECTION_ENABLED directive declared as a result in the project .pch file.

This process of automatically inserting the above macros is robust but can likely always be improved. If you have any particular examples of code which is patched incorrectly or any other problem using or suggestion to improve "Injection", do please contact the developer so I can rectify it using the email address here. These instructions will close automatically when you open a project but you can always view the again by using the "File/Introduction" or "Help" menu items.

Tunable Run-time Parameters

While an application is connected to Injection, 5 tunable parameter values and 5 Color Wells are available to the application through the global arrays INParameters[] and INColors[]. These can be used in to tune your code during development and modified in real time using the sliders in the "Parameters" pop-down panel. Object oriented access to these parameters is available using the +[NSObject inParameter:tag] method. You can also set a delegate using +[NSObject setDelegate:delegate forInParameter:tag] and receive -[delegate inParameter:tag hasChanged:value] notifications as the sliders are moved. There is also an image well which can inject an image into your appplication through the INImageTarget variable. For more information see the BundleInterface.h and BundleInjection.h headers in the Injection Application bundle which contain the full application-side implementation.

OS X, iOS and Xcode are trademarks of Apple Inc.

QuickStart Guide

To get started using Injection, follow these steps:

  • Launch Injection!

  • Open this guide in a web browser (as it will close automatically when you open a project.)

  • Download the UICatalog or GLEssentials example project and unzip it.

  • Drag the UICatalog/UICatalog.xcodeproj or GLEssentials/XXXGLEssentials.xcodeproj file onto the Injection Icon to open and convert it for run-tine loading.

  • Click Open App Project button to open the project in Xcode and edit the .pch file for the project to remove the #ifdef DEBUG to ensure injection is enabled.

  • Build and run the application in the iOS Simulator. When the application starts it will connect to Injection and the "Build" button status will go green.

  • Make a change to a file and save it. Injection will pick up the change, roll the class changed into a bundle and message the application to load it, applying the changes made.

  • If linking errors are displayed in the Injection window make sure the main bundle project does not have the 'Symbols Hidden by Default' build option set. If so rebuild and launch the application. Other linking errors can be caused by missing frameworks or using the incorrect architecture, compiler or memory model in the bundle project created to inject the code. Click the Open Bundle Project button to edit the sub-project and make any changes required until it builds. Any problems actually loading the bundle will be displayed in the debug console of the client application itself.

  • To inject code into an application running on an iOS device an additional manual step is required. Add the following as a "Run Script" step in the target "Build Phases" tab:

    echo "$CODESIGNING_FOLDER_PATH" >/tmp/"$PROJECT_NAME.$USER" && echo "$CODE_SIGN_IDENTITY" >>/tmp/"$PROJECT_NAME.$USER" && exit;

    This script allows Injection to know the build location of the app before it is copied to the device and the code signing identity being used. Rebuild the application and you can then use Injection as before on an actual device.