Hallo vrienden, Leo hier. The topic today is how to use Dependency Injection using Property Wrappers in Swift.

Dependency Injection is a very old programming topic. No wonder we have so many resources about it on the web, and it can be used to help you accomplish the old saying: Compositions over inheritance. I’m not saying that inheritance is something inherently bad, it has its place in the sun when used properly. I personally prefer compositions because we reduce coupling and it’s easier to test each part.

There are many articles, many tutorials, and many frameworks that explain how can you do dependency injection in Swift. With initializers, closures, singletons, property wrappers, remote dependencies, static subscripts, extensions…. all the flavors you want.

Today we will explore a property wrappers solution, and if you are familiar with Android development, that looks like the android Dagger.

Disclaimer: This wasn’t stress tested and it’s just a starting point of a type of dependency injection technique. Be careful importing directly to your projects.

No more talking let’s write it down, but first…

 

Painting of The Day

This painting is a 1907 masterpiece from Alexandre Benois called Military Parade of Emperor Paul in front of Mikhailovsky Castle.

Alexandre Nikolayevich Benois, was born on 3 May 1870 in Saint Petersburg and died on 9 February 1960 in Paris. Was a Russian artist, art critic, historian, preservationist, and founding member of Mir Iskusstva (World of Art), an art movement and magazine.

Created with more detail than most of his works, Benois illustrates the still formality of the parade by filling the sky with lightly falling snow. Although Emperor Paul is not within view, his presence is indicated by the lamppost and barricade in the foreground, pulling the viewer into the painting by indicating that they are within reach of their ruler.

Today we will talk about property wrappers, and this painting is literally showing how can real property can be wrapped by royalty and the military force.

 

The Problem – Dependency Injection using Property Wrappers in Swift

You want to have an easy way to inject dependencies into your classes and structs.

Let’s start from the beginning. So in the end what do we want to achieve? In the end, we want to inject classes into our view model so this way we don’t need to worry about that in the view model initializer. We should aim for this:

struct UserPhotoViewModel {
    
    @Inject<PhotosManaging> private var photoManager: PhotosManaging
    @Inject<OldPhotosManaging> private var analogicPhotoManager: OldPhotosManaging
    
    func makeItWork(){
        print(photoManager.getAllPhotos())
        print(analogicPhotoManager.getAllOldPhotos())
    }
}

In our example, we want to create a UserPhotoViewModel that needs managers to work properly, the PhotosManager and the AnalogicPhotoManager. But how can we do that?

 

Diving in Dependency Injection

Let’s start with the protocols:

protocol Component: AnyObject {
    init()
}

protocol PhotosManaging: Component {
    func getAllPhotos() -> [String]
}

protocol OldPhotosManaging: Component {
    func getAllOldPhotos() -> [Int]
}

These three protocols will serve as a solid type base for what we want to achieve. The PhotosManaging and OldPhotosManaging both conform to the Component. The Component guarantee to us that any manager can be made without having to pass anything to it.

Now that we have the protocols let’s create the managers.

final class PhotoManager: PhotosManaging {
 
    init() {}
    
    func getAllPhotos() -> [String] {
        return ["Photo A", "Photo B", "Photo C"]
    }
}

final class AnalogicPhotoManager: OldPhotosManaging {
    
    init() {}
    
    func getAllOldPhotos() -> [Int] {
        return [323, 2, 5555]
    }
}

Here I just put some stub output to show how this could be used. Nothing too complex.

 

The Resolver of the Dependency Injection

Now we will do the Resolver class. The resolver class as its name says resolves types for you. So with the resolver, you can do two things: add new injectables components and get those components. We will create it as a Singleton, but you can do it whatever you want.

class Resolver {
    private var dependencies = [String: AnyObject]()
    private static var shared = Resolver()
    
    static func register<T>(_ dependency: T) {
        shared.register(dependency)
    }
    
    static func resolve<T>() -> T {
        shared.resolve()
    }
    
    private func register<T>(_ dependency: T) {
        let key = String(describing: T.self)
        dependencies[key] = dependency as AnyObject
    }
    
    private func resolve<T>() -> T {
        let key = String(describing: T.self)
        let dependency = dependencies[key] as? T
        
        assert(dependency != nil, "No dependency found")
        
        return dependency!
    }
} 

The next thing is the cherry of the cake, the Property Wrapper:

@propertyWrapper
struct Inject<T> {
    var wrappedValue: T
    
    init() {
        self.wrappedValue = Resolver.resolve()
    }
}

Now that we have a way to search and get any component we just need to wrap it into a property wrapper. The initialization is the key here, we get the component from the resolver without having to explicit it anywhere here. It’s all about the generics!

The structure is done! Now let’s test our solution:

Resolver.register(PhotoManager() as PhotosManaging)
Resolver.register(AnalogicPhotoManager() as OldPhotosManaging)

let userPhotoViewModel = UserPhotoViewModel()

userPhotoViewModel.makeItWork()

The result should be:

We are done!

 

Summary – Dependency Injection using Property Wrappers in Swift

Today we study one way to do dependency injection. This way has the drawback that we can only inject things that have an empty initializer, who knows in the future I can bring other types of dependency injection that you can inject anything. Thanks to the user @blackjacx for pointing out my mistake here!

That’s all my people, I hope you liked reading this article as much as I enjoyed writing it. If you want to support this blog you can Buy Me a Coffee or leave a comment saying hello. You can also sponsor posts and I’m open to freelance writing! You can reach me on LinkedIn or Twitter and send me an e-mail through the contact page.

Thanks for reading and… That’s all folks.

Credits:

title image