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.
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.
@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 Macro | Building Project | Definition 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.
OS X, iOS and Xcode are trademarks of Apple Inc.
To get started using Injection, follow these steps:
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.