Git Product home page Git Product logo

injectionforxcode's Introduction

Icon Injection Plugin for Xcode

Copyright (c) John Holdsworth 2012-16

TLDR:

Injection for Xcode is an Xcode plugin (available via Alcatraz) or AppCode that dynamically 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.

Injection Example

Announcements of major additions to the project will be made on twitter @Injection4Xcode.

How to Use Injection for Xcode

For installation and usage for AppCode see below. If you're a visual learner, you may appreciate this video post from @Orta covering the basics.

With Xcode, either install via Alcatraz, or install by cloning this repo and build InjectionPluginLite/InjectionPlugin.xcodeproj.

The plugin can be removed either via Alcatraz, or by running: rm -rf ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins/InjectionPlugin.xcplugin

Simple Proof of Concept Once Installed

Once it is installed, compile and run a project as normal. From here you should take any class that would exist when your app is loaded, add a new function - injection and add a breakpoint on that line.

- (void)injected
{
    NSLog(@"I've been injected: %@", self);
}

or

func injected() {
    print("I've been injected: \(self)")
}

Then press ctrl+=, and you'll see Xcode stop at the breakpoint. You've just injected new code into a running app. Awesome right?

Callbacks in Your Code

You can build on top of Injection from three callbacks:

  • - (void)injected as an instance method, which gives you the chance to re-update an object with new contexts.
  • + (void)injected as a class method, making it possible to update globals with new information
  • Listening for INJECTION_BUNDLE_NOTIFICATION, allowing other classes to listen out for injection calls, this is useful for providing app level changes.

If you are interested in diving even deeper into callbacks, check out Tunable Parameters.

Tunable Example

Swift Support

Swift support works great when working with a collection of classes. However, there are a number of cases where re-injection won't work elegantly with Swift:

  • Making changes to Structs.
  • Changing func or classes that are marked as final.
  • Global func or variables that are not constrained into a class.

In day-to-day development against Cocoa/UIKit, it's rare, but possible to hit these cases, for more information see What Happens With Swift?

How it Works

Injection for Xcode is an extension to the Xcode IDE that allows you to patch the implementation of a class's method without having to restart the application.

It performs this by parsing the build logs of the application to determine how a source file was last compiled. With this it wraps the result of re-compiling into a bundle which is injected into the application using the dynamic loader. At this point there are two versions of a class in the app, the original and a new modified version from the bundle. The modified version is then "swizzled" onto the original class so changes take effect.

This swizzling takes advantage of the fact that Objective-C binds method invocations to implementations at run time. This can also be performed on Swift classes provided that the method or class is not final or private (i.e. the method can be overridden) by patching the class' "vtable". This excludes the injection of methods of structs.

What Else Does This Plugin Do?

  • There is support for working specifically with Storyboard-based iOS projects.

  • The plugin offers a way to quickly change a collection of tunable parameters

  • Xcode is given a badge, showing the number of active Injection connections to apps.

  • When you start using Injection, a new Xcode Project is added to the same folder as your project (either iOSInjectionProject or OSXInjectionProject.) This is the xcode project base for the changes which are injected into your project, it is recommended to add this to your .gitignore.

  • The injection key command can be customised from ctrl+= in the "Tunable App Parameters" panel.

  • Works on a device, if you apply a patch to your project..

What Happens with Swift?

Icon

Swift, presents a few more stumbling blocks for the uninitiated. Provided that methods are of a non final class and are non final (this excludes structs alas) they can be injected. In this example the sharedInstance variable is declared static rather than "class" to make sure it is not injected to ensure there is only ever one singleton. For the "injected" methods to work your class must inherit from NSObject.

More problematic is the more common use of variables or functions outside a class which are referred to across the files of a bundle. Swift 1.2+ takes the view these "internal" scope symbols should not be available across bundles and are made "private extern" in their object file making them unavailable at run time. This means that the above code will inject but injecting another file referring to the dispatch_on_main function will fail with obscure dynamic loading errors.

The simplest solution is to make these variables and functions public though, for a framework, this may be unsatisfactory. The alternative is to patch the object files of the project to remove the private extern flag and relink the bundle. In order to do this a script ~/bin/unhide.sh is created by the plugin build which should be called as an additional "Run Script" build phase after linking your app to perform this patch and relink.

Use with AppCode

Injection can be used from inside AppCode provided the application has been patched and you have previously injected that project from inside Xcode to set up a link to the build logs.

To install, copy the jar file InjectionPluginAppCode/Injection.jar from this repo to ~/Library/Application Support/AppCode3*. You’ll need to re-patch the project from inside AppCode as it uses a different port number to connect.

Limitations of Injection

There are limitations of course, largely centering around static variables, static or global functions and their Swift equivalents. Consider the following Objective-C code.

Icon

  • One potential problem is when the new version of the class is loaded, it comes with it's own versions of static variables such as sharedInstance and the once token. After injection has occurred, this would generate a new singleton instance.

    To prevent this, class methods with the prefix "shared" are not swizzled on injection to support this common idiom.

  • 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.

    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.

    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 you may miss callbacks in your app.

  • The function dispatch_on_main does not inject, as it has been statically linked into the application. It does however, inject by proxy in the case shown via the doSomething method. dispatch_on_main will have been linked locally to a version in the object file being injected.

"Nagware" License

This source code is provided on github on the understanding it will not be redistributed. License is granted to use this software during development for any purpose for two weeks (it should never be included in a released application!) After two weeks you will be invited to make a donation $10 (or $25 in a commercial environment) as suggested by code included in the software.

If you find (m)any issues in the code, get in contact using the email: support (at) injectionforxcode.com

Please note:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

injectionforxcode's People

Contributors

dbgrandi avatar hmml avatar johnno1962 avatar mgamer avatar orta avatar readmecritic avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.