Elevate @AssistedInject to new heights in your Android project

Reading Time: 4 minutes

Everybody used a form of DI in their projects, Android developers use(d) Dagger2/Hilt at least once in their careers.

Throughout this experience, your life was simplified and just one @Inject saved you from writing boilerplate code. This wasn’t always the case, you needed to add something dynamically in one of your dependencies, this is where @AssitedInject comes into play.

An assisted injection is a dependency injection (DI) pattern that is used to construct an object where some parameters may be provided by the DI framework and others must be passed in, at creation time (a.k.a “assisted”), by the developer (you).

The assisted injection uses a factory to provide your assisted dependency, the steps are as follows:

  1. Annotate your dependency with @AssitedInject
  2. Provide the dependencies that can be automatically wired by the DI library
  3. Annotate your dynamically added dependencies with @Assisted and provide them with a name if needed
  4. Create a factory for your dependency annotated with @AssistedFactory and a function that creates and returns your assisted dependency

To facilitate the aforementioned steps, in this blog post you’ll now build a reusable one shot shared preferences dependency.

In order to have our “OneTimePreference”, we create a common contract so that each dependency that implements it will behave as agreed.

interface OneTimePrefContract {
    val isOneTimeShown: Boolean
    fun setOneTimeShown()
    val oneTimePrefs: SharedPreferences

The real implementation comes in a form of an “assisted” dependency that implements the contract and is provided from a factory.

class OneTimePref @AssistedInject constructor(
    @ApplicationContext private val context: Context,
    @Assisted(PREFS_TAG_KEY) private val prefsTag: String,
    @Assisted(PREFS_BOOLEAN_KEY) private val prefsBooleanKey: String
) : OneTimePrefContract {
    private companion object {
        private const val PREFS_TAG_KEY = "prefsTag"
        private const val PREFS_BOOLEAN_KEY = "prefsBoolean"
    interface OneTimePrefFactory {
        fun create(
            @Assisted(PREFS_TAG_KEY) prefsTag: String,
            @Assisted(PREFS_BOOLEAN_KEY) prefsBooleanKey: String
        ): OneTimePref
    override val oneTimePrefs: SharedPreferences
        get() = context.getSharedPreferences(
    override val isOneTimeShown get() = oneTimePrefs.getBoolean(prefsBooleanKey, false)
    override fun setOneTimeShown() = oneTimePrefs.edit { putBoolean(prefsBooleanKey, true) }

As you see the factory provides the same assisted parameters that are needed in order for the assisted inject to happen while having an external dependency from the outside as with our application context.

You can now reuse it anywhere.

class WalkThroughFragment : Fragment (){
	lateinit var oneTimePrefFactory : OneTimePref.OneTimePrefFactory

	private val walkThroughPreferences : OneTimePref by lazy {
		oneTimePrefFactory.create("walkthrough-prefs", "walkthrough-isShown") // consider using constants, this is for demonstration purposes only

Congratulations, you’ve learned @AssistedInject

There is one limitation by the DI framework and one big issue with this code.

  • @AssistedInject dependencies can’t be scoped
  • This is a lot of boilerplate to write

In order to write less boilerplate Kotlin’s delegation is one hell of a powerful tool to know and we want our dependency to be scoped to the lifecycle of a Fragment (for demonstration purposes).

class WalkThroughPrefsProvider @Inject constructor(
    private val oneTimePrefFactory: OneTimePref.OneTimePrefFactory
) : OneTimePrefContract by oneTimePrefFactory.create(
) {
    private companion object {
        private const val WALK_THROUGH_PREFS = "walkThrough"
        private const val WALK_THROUGH_PREFS_SHOWN_KEY = "walkThroughKey"

Now you can go around injecting your WalkThroughPrefsProvider and having more readable code.

class WalkThroughFragment : Fragment (){
	lateinit var oneTimePrefFactory : WalkThroughPrefsProvider

The code is publicly available as a Gist.

Dependency injection and how I use it in Vaccination iOS app

Reading Time: 5 minutes

In programming, dependency injection is a technique where one object serves dependencies to another object. The concept is that instead of the client object to decide what kind of service it will use, another object tells to the client what service he has to use.

We can see the dependency injection as a software pattern. The fundament of this pattern is passing the service or object to the client, instead of allowing the client to find or to build the service on his own. What is the advantage of using this pattern? The main pros of this pattern are the readability of the code and code reusability.

Dependency injection – Injector example

Dependency injection is one form of the broader technique of inversion of control. The client delegates the responsibility of providing dependencies to external code (the injector) (Figure 1). The client is not allowed to call the injector code; it is injecting code that constructs the services and calls the client to inject them. This means the client code does not need to know about the injecting code, how to construct the services or even which actual services it is using; the client only needs to know about the intrinsic interfaces of the services because these define how the client may use the services. This separates the responsibilities of use and construction.

Types of DI

There are 3 types of dependency injection:

  1. Constructor injection: the dependencies are provided through a class constructor
  2. Setter injection: the client exposes a setter method that the injector uses to inject the dependency
  3. Interface injection: the dependency provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a setter method that accepts the dependency

Vaccination app example

In the iOS world, the constructor injection is known as an initializer-based injection. This concept is realized with injecting the dependency object (or service) during initialization of the client class and this dependency is consistent/unchangeable during the life cycle of the client object.

In the previous few months, I’ve worked on the vaccination iOS application for N47 and I’ve decided to use the popular MVVM pattern inside. In the core of this pattern is the dependency injection. The components of the pattern are Model, View, and ViewModel, and each component is responsible for a different thing in the app. The point is to make the code more modular and easy to test.

The ViewModel (VM) component is a structure that contains only the data needed by the View component. The View component is presenting the data injected by the ViewModel. The ViewModel at other side is created by injecting dependency from the Model component. The main advantage of the MVVM is that we are creating views that have only one goal – presenting data. The view itself is not aware of the other task like fetching, persisting, etc.

We can see the initializer-based injection in action with the real example used in the Vaccination Demo App of N47. Let’s see first how the Details ViewModel looks like:

struct VaccineDetailsViewModel {
    let title: String
    let description: String
    let date: String?

The vaccine details view only needs title, description, and date for the vaccine. It doesn’t need more information. On the other hand, the vaccine model can contain more details about the vaccine, but this information is useless for the View. Inside the view controller (View component) we define view model property and set it via controller initializer. We can see this in the code snippet below:

 var vacineViewModel: VaccineDetailsViewModel?

class func createController(viewModel: VaccineDetailsViewModel?) -> VaccinesDetailViewController {
        let controller = VaccinesDetailViewController(nibName: "VaccinesDetailViewController", bundle: nil)
        controller.vacineViewModel = viewModel

        return controller

This type of injection is preferable because it keeps us the safety of creating incomplete objects and with that, we will avoid coding mistakes.
So when I want to create a controller that will present the details for the vaccines and the scheduled vaccines I’m using injection via initializer in this way:

let details = VaccinesDetailViewController.createController(model: vaccination.createModel())

Other DI types in action…

In the Vaccination App, I’m also using Dependency Injection via setter creating the UITableView cells.

var vaccinationData: Vaccination? = nil {
        didSet {
            guard let vaccineId = vaccinationData?.vaccineId else { return }
            guard let vaccine = VaccineManager.sharedInstance.getVaccineById(vaccineId: vaccineId) else { return }
            let language = ModuleSharedPreferences.shared.language.rawValue
            let translation = vaccine.translations[language]
            vaccineTitleLabel.text = translation?.name
            vaccineApplyDateLabel.text = vaccinationData?.date

The code snippet above shows the vaccination data object that should be set with setter if we want the cell to be populated with data. Here is the code that will do the magic:

        let cell = tableView.dequeueReusableCell(withIdentifier: VaccinesTableViewCell.cellIdentifier, for: indexPath) as! VaccinesTableViewCell
        let vaccination = vaccinationList[indexPath.row]
        cell.vaccinationData = vaccination


Dependency injection is a powerful technique. Our code becomes more readable, reusable and easy for testing. We were able to see this technique in action in a real project and it was used within the popular design patterns MVVM. Using this technique we become sure that our components/services are completed, fully created before we start to use it.