Website Loader
Fork me on GitHub
Homepage Top Icon

Typhoon

Dependency injection for Objective-C & Swift

A project by AppsQuick.ly

Homepage Bottom Icon

READ MORE ABOUT TYPHOON

Read More About Us

Get ready!
You’re about to be blown away

Description

What is Dependency Injection?

Dependency Injection

Many people have trouble getting the hang of dependency injection, at first. And I think part of the problem is that it is actually so simple that we’re inclined to look for something more complicated. “Surely that there has to be more to it?!”, so to say. So, with that in mind, imagine that you’re writing an app that gives weather reports. You need a cloud-service (excuse the pun ;) )

to provide the data, and at first you go for a free weather report provider, but in future you’d like to integrate a weather service with better accuracy and more features. So, as do all good object-oriented developers, you make a WeatherClient protocol and back it initially with an implementation based on the free, online data provider.

Without dependency injection, you might have a View Controller like this:

-(id) init
{
 self = [super init];
 if (self)
 {
 //The class using some collaborating class builds its own assistant.
 //it might be one of several classes using the weatherClient. 
  _weatherClient = [[GoogleWeatherClientImpl alloc] initWithParameters:xyz];
 }
 return self;
}

The thing with this approach is, if you wanted to change to another weather client implementation you’d have to go and find all the places in your code that use the old one, and move them over to the new one. Each time, making sure to pass in the correct initialization parameters. A very common approach is to have a centrally configured singleton:

_weatherClient = [GoogleWeatherClient sharedInstance];

With either of the above approaches, in order to test your view controller, you now have to test its collaborating class (the weather client) at the same time, and this can get tricky, especially as your application gets more complex. Imagine testing Class A, depends on Class B, depends on Class C, depends on… Not much fun! Sure, you could patch out the singleton with a mock or a stub, but this requires peeking inside the code to find the dependencies. Besides taking time that could be better spent else-where, this ends up becoming “glass-box” testing as opposed to “black-box”

testing. Isn’t it better to be able to test the external interface to a class, without having worry about what’s going on inside? And you have to remember un-patch again at the end of the test-case or risk strange breakages to other tests, where it’s difficult to pin-point what the real problem might be. So with dependency injection, rather than having objects make their own collaborators, we have them supplied to the class instance via an initializer or property setter.

And now, it simply becomes:

-(id) initWithWeatherClient:(id<WeatherClient>)weatherClient
{
 self = [super init];
 if (self)
 {
     _weatherClient = weatherClient;
 }
 return self;
}

Is that all they mean by ‘injected’?

Yes it is. Right now, you might be thinking “Geez! That’s a pretty fancy name for something so plain.” Well, you‘d be right. But let‘s look at what happens when we start to apply this approach: Let's say you identify some hard-wired network configurations in a your GoogleWeatherClient, and correct this by instead passing them in via an initializer method. Now if you want to use this class, as a collaborator in a new class, let's say a ViewController, then your GoogleWeatherClient itself can be either a hard-wired dependency, or injected. To get the benefits of dependency injection again, we repeat the process, pulling up the class and along with its own dependencies. And we keep applying until we have a logical module or 'assembly'.

In this way dependency injection lets your application tell an architectural story. When the key actors are pulled up into an assembly that describes roles and collaborations, then the application’s configuration no longer exhibits fragmentation, duplication or tight-coupling. Having created this script that describes roles and collaborations we realize a number of benefits.

Benefits of Dependency Injection

  • We can substitute another actor to fulfill a given role. If you want to change from one implementation of a class to another, you need only change a single declaration.
  • By removing tight-coupling, we need not understand all of a problem at once, its easy to evolve our app’s design as the requirements evolve.
  • Classes are easier to test, because we can supply simple mocks and stubs in place of concrete collaborators. Or the real collaborators, but configured to be used in a test scenario.
  • It promotes separation of concerns and a clear contract between classes. Its easy to see what each class needs in order to do its job.
  • We can layout an app's architecture using stubs - the simplest possible implementation of a role - so that we can quickly see an end-to-end use-case taking place. Having done this, we can assign to other team members the responsibility of filling out these stubs for real implementations, and they can do so without breaking the app while they work, and without impacting other team members.

Your Dependency Injection Options

If you proceed with the Dependency Injection pattern (assuming you’re not one of the remaining “flat-earthers”, who believe that Objective-C somehow magically alleviates the need for common-sense: “Oh, I don’t do DI, I use swizzling class-clusters!”), then there are basically two options:

You can do dependency injection without a framework to help you. It is simple after all, and in fact I recommend you do this, at least as an exercise to fully appreciate the pattern. Still, just as you can also write tests without a test framework, or mocks without a mocking library, once an application gets more complex its good to have help if you can get it - to put the pattern on "rails", so to speak (excuse the pun).

So, going down the framework route, there’s been quite a lot of action in Objective-C land, over the last three years. In fact, there are now around 15 Dependency Injection frameworks, many following in the footsteps of Google Guice. The authors have done a great job (Objection is especially good). However, I wanted an approach that allows the following:

Design Goals & Features

Features

Non-invasive. No macros or XML required. Uses powerful ObjC runtime instrumentation.

No magic strings – supports IDE refactoring, code-completion and compile-time checking.

Provides full-modularization and encapsulation of configuration details. Let your architecture tell a story.

Dependencies declared in any order. (The order that makes sense to humans).

Makes it easy to have multiple configurations of the same base-class or protocol.

Supports injection of view controllers and storyboard integration.

Supports both initializer and property injection, plus life-cycle management.

Powerful memory management features. Provides pre-configured objects, without the memory overhead of singletons.

Excellent support for circular dependencies.

Lean. Has a very low footprint, so is appropriate for CPU and memory constrained devices.

While being feature-packed, Typhoon weighs-in at just 3000 lines of code in total.

Battle-tested — used in all kinds of Appstore-featured apps.

Mod Productions and The Australian Chamber Orchestra release the Typhoon-powered ACO VIRTUAL, and are awarded AppStore Best New Apps

SAMPLE APPLICATION

Read More About Us

Typhoon lets your application tell an architectural story, modularizing configuration that would otherwise be duplicated.

Pocket Forecast

Take a look at the code in this example app.

FEATURES

Returns weather reports from a remote cloud service.

Caches weather reports locally, for later off-line use.

Stores (creates, reads, updates deletes) the cities that the user is interested in receiving reports for.

Can use metric or imperial units.

Running the sample

Clone this repository, open the Xcode project in your favorite IDE, and run it. It’ll say you need an API key.

Get an API key from WorldWeatherOnline

Using your API key, set the application configuration.

Run the App in the simulator or on your device and proceed to the exercises here.

Start using Typhoon! Check out the User Guide.

WHO IS BEHIND IT?

Read More About Us

“My passion is to present complex requirements in a way that is simple and intuitive to the user.” — Jasper Blues

Meet The Contributors

Typhoon is sponsored and lead by AppsQuick.ly with contributions from around the world.

Our Team

Jasper
Blues

Founder / Proj. Lead

I've been programming for over 30 years. Vector graphics in 6502 assembly? Now that's where its at! Or was ;)

Our Team

Aleksey
Garbarev

Principal Developer

Perfectionist. I like challenging performance problems — optimising code until it achieves the best possible speed.

Our Team

Egor
Tolstoy

Contributor

"I'm interested in clean software design and security aspects of iOS development.

Our Team

Herman
Saprykin

Contributor

I lead the development team at Rambler & Co.

Our Team

Igor
Vasilenko

Contributor

I'm an iOS developer from St Petersburg, Russia, working on apps like Raiffaisen Bank and Cryptopay.

. . . and a huge thanks to our past contributors Jeff Roberts, Mike Owens, Robert Gilliam, Erik Sundin and Daniel Rodríguez.

It’s Q&A Time!

Read More About Us

Got any questions?

Frequently Asked Questions

Something’s not clear? Check Below!
We tried to answer any question you might have.

Q. Madrid, Manila, Omsk, Phnom Penh, San Francisco. . . What do these cities have in common?

A. We get this one all the time.

These are the some of the cities where Typhoon contributors live. It goes to show: you never know where you’ll find a ninja coder lurking these days. They’re also the default cities in Pocket Forecast - the Typhoon sample application. Incidentally, as I’m writing this its +41° celsius in Phnom Penh and -30° in Omsk!

Q. I’ve started using Typhoon, now I’ve got a technical question. Where can I get help?

A. If you can't find what you need in the User Guide, then there’s a Typhoon tag on StackOverflow, which is monitored by Typhoon users and contributors. Chances are your question can be answered there.

Q. Compatibility

A. Typhoon can be used with OS X and iOS. It has not been tested with GnuStep. It can be used with or without ARC, but not with garbage collection.

Internally, Typhoon relies on some weak references which where introduced in iOS 5.0 (iPhone3gs and up, iPad 1st generation and up) and OSX 10.7 (most Macs built since 2009).

Q. Isn’t Objective-C a dynamic language?

A. Yes, and I love categories, method swizzling, duck-typing, class clusters, associative references in categories, and all that cool stuff. None of these are replacements for DI.

Q. Doesn’t Dependency Injection Just Shift Coupling Elsewhere?

A. If by elsewhere you mean from everywhere to one place, and (in the case of statically compiled languages) from compile-time to runtime … then yes! That is the purpose of dependency injection.

Q. But I don't need DI, right? I can just pass arguments to init

A. Well this is how you start. A good first move. But remember that systems are composed of collaborating objects, each with a given responsibility. So let's say you identify some hard-wired dependencies in Class A, and correct this by instead passing them in via an initializer (preferable) or or perhaps property setters or methods. Now if you want to use this class, as a collaborator in a new class, then it can be either a hard-wired dependency, or injected.

To get the benefits of dependency injection again, we repeat the process, pulling up the class and along with its own dependencies. And we keep applying until we have a logical module or 'assembly'. Once we have this module it tells an 'architectural story' that explains the key characters in the system. If we want to replace the component performing, eg the UserManager role for another we can do so by modifying this script. Other benefits? Good cohesion and maintainability; simplifies unit and integration testing.

Q. Does Typhoon work well on resource-constrained devices?

A. Yes! At start-up Typhoon consumes about the same resources as parsing a JSON response from a network request. Also, Because Typhoon allows the use of prototype-scoped components instead of singletons, components that aren’t required can be deallocated when they’re not being used.

In fact, the award-winning ACO Virtual’s controller app was deployed on iPad 1s, because these represented very good value for money at the time hardware was being purchased. We received a lot of compliments about this app’s stability.

Q. Can Typhoon inject view controllers?

A. Injecting. Others might consider it banal and somewhat of a drag, but Typhoon actually gets-off on it. Typhoon lives to inject. Once while travelling through a rough-part-of-town in a foreign city, Typhoon and I happened upon a psychopath-gangster-singleton from the 80s. This thing was seriously intimidating and widely reported in the press as being ‘uninjectable’. Well Typhoon injected the shit out of it without even breaking a sweat.

View controllers? This is child’s play for Typhoon. Initializers and properties, xibs or no xibs and with or without Storyboards. You can also use it to build complex views, if you like. Take a look at the sample app.

Q. What other customizations have been done for applying DI in a mobile or desktop environment?

A. Quite a few — we’ve worked hard on this. One example is Typhoon's memory management. Another is transitioning from one use-case’s object-graph using runtime arguments.

Does Typhoon use Swizzling?

A. Only if you use the plist integration feature.

Its used in the plist plist style of instantiation for iOS. This is so bootstrapping occurs early enough that UIStateRestoration works correctly in Storyboards. The alternative, generic style of boot-strapping Typhoon does not provide any special integration and can be used if you do not need UIStateResotoration.

We used to use it to proxy assembly methods, but v4 replaced that with NSProxy, because it is faster.

Q. What’s with the name Typhoon?

A. Over the 2012-13 new year, I had a family holiday booked at one of our lovely beaches… but it turns out there was a late-in-season Typhoon. So I stayed home and wrote this instead… Also it was inspired by the Spring Framework. In temperate climates we have Spring, Summer, Autumn & Winter. But here in the Philippines we have hot-season, sizzling-hot-season and Typhoon-season ;).

Update: Since then we have had the incredibly destructive super-typhoon Haiyan, which has been very sad. Our sincere condolensces to all those affected.