Skip to content

Instantly share code, notes, and snippets.

@AncAinu
Last active July 24, 2018 11:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AncAinu/a262b93e79f682624d52204d192d2879 to your computer and use it in GitHub Desktop.
Save AncAinu/a262b93e79f682624d52204d192d2879 to your computer and use it in GitHub Desktop.
Dependency injector for MVVM architectures in swift

Injector.swift

Dependency injector for MVVM architectures in swift

Little tool useful to avoid boilerplate code while passing down your different services between view models.

If you like to use plain Dependency Injection, meaning passing your dependencies in the initialiser, but you often end up with very fat initialiser, then this is for you.

Problem

In this example, we see the initialiser stacking up services. And this goes even worse if that view model creates other view model that needs different services!

class ViewModel {
	let apiService: ApiService
	let imageService: ApiService
	let fileService: FileService
	
	init(apiService: ApiService, imageService: ApiService, fileService: FileService) {
		self.apiService = apiService
		self.imageService = imageService
		self.fileService = fileService
	}
}

Usage

We reduce the initialisation to only 1 parameter, which will always be the same between view models.

class ViewModel {
	let injector: Injector
	let apiService: ApiService
	let imageService: ApiService
	let fileService: FileService
	
	init(injector: Injector) {
		self.injector = injector
		self.apiService = injector.dependency()
		self.imageService = injector.dependency()
		self.fileService = injector.dependency()
	}
}

Bonus

If you are fine with having your dependencies as variables, then it gets really straightforward:

class ViewModel {
	let injector: Injector
	lazy var apiService: ApiService = injector.dependency()
	lazy var imageService: ApiService = injector.dependency()
	lazy var fileService: FileService = injector.dependency()
	
	init(injector: Injector) {
		self.injector = injector
	}
}

Keep in mind

This doesn't do magic, you will likely pass down all your dependencies to all your view models. It is only a nice piece of code to remove some boilerplate, keep in mind release count management.

//
// Injector.swift
//
// Created by Tancrède Chazallet on 11/07/2018.
// Copyright © 2018 BitBuildr Ltd. MIT Licence, feel free to use.
//
import Foundation
class Injector {
private let dependencies: [AnyObject]
init(dependencies: [AnyObject]) {
#if DEBUG
dependencies.forEach({ dependency in
guard dependencies.filter({ dependency.self === $0.self }).count == 1 else {
fatalError("Injector was given 2 dependencies of the same type")
}
})
#endif
self.dependencies = dependencies
}
func dependency<T>() -> T {
guard let dependency = dependencies.first(where: { $0 is T }) as? T else {
fatalError("Couldn't retrieve dependency of type \(T.self) in:\n\(dependencies.debugDescription)")
}
return dependency
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment