Dependency injection with property wrappers

Feel the relief of one less third-party library — tested.

Bruno Muniz
2 min readNov 4, 2020

Today we’ll go over how to use dependency injection with Swift 5.1 property wrappers. This approach is particularly interesting because its clean and very lightweight since it doesn’t require any third-party library.

Photo by Wind Ira on Unsplash

When using property wrappers, we need to declare the wrappedValue. In the initializer we are resolving the dependency which will bring us the instance that was stored in the container for that type.

The container holds a private static variable which will be used on static functions such as resolve and register and it also holds a private dictionary for the dependencies. As you can see below, the dictionary has its key type as String because after registering dependencies we want to be able to retrieve them using their type as the key.

An example of a dependency is a coordinator. (Kudos to Soroush Khanlou)

With the precondition we created at line 31 from the container, we explicit tell the other developers that we need to register the dependency before initializing anything that holds a reference to it. We can register our coordinator at didFinishLaunchingWithOptions on AppDelegate but as our codebase expands we’ll probably want to put those somewhere else.

That’s it.

Testing

This coordinator doesn’t have any dependencies, so when we test it there’s no need to inject anything:

Now, when testing our screen which has the coordinator dependency we want to register a double coordinator that conforms to DashboardCoordinating protocol prior to the screen init. At this point we can either override the registered dependency or disable AppDelegate as main.

Override

The container that holds the registered dependencies is a dictionary, so it can override the instance by using the same key (which is just the same protocol). In another words, if we register an instance of DashboardCoordinator as DashboardCoordinating and later on register an instance of DashboardCoordinatorDouble as DashboardCoordinating, the last one will override the first one.

Skip AppDelegate

There’s a trick that we can do in order to skip AppDelegate from our tests.

  1. Remove @UIApplicationMain from your AppDelegate
  2. Create a main.swift file
  3. Import UIKit and check for ‘existance’ of XCTestCase class. This is basically checking if its running on a test target or not.
  4. Write the actual main.

Now that we skipped AppDelegate when running a test target, we don’t have the real dependency registered which is fine because we’ll register the double coordinator before initializing the screen we want to test and the cool thing of this trick is exactly that — we must make sure we’re using the proper dependencies on our tests.

--

--