Skip to content
This repository was archived by the owner on Jun 20, 2023. It is now read-only.

Conversation

@d4rken
Copy link
Member

@d4rken d4rken commented Sep 14, 2020

Description

This PR adds the boilerplate necessary for combining ViewModels and dependency injection. As guide/demo the TestRiskLevelCalculationFragment has been refactored.

Our injected "ViewModels" are called VDC (View Data Controller/Component) because "ViewModel" is a confusing name and overloading the existing viewModels extensions would lead to confusion.

Using a VDC requires 3 things:
In a dagger Module we need to add a VDC.Factory to a lookup map:

@Module
abstract class TestRiskLevelCalculationFragmentModule {
    @Binds
    @IntoMap
    @VDCKey(TestRiskLevelCalculationFragmentVDC::class)
    abstract fun testRiskLevelFragment(factory: TestRiskLevelCalculationFragmentVDC.Factory): VDCFactory<out VDC>
}

In the Fragment we need to instantiate it

    @Inject lateinit var vdcSource: VDCSource.Factory
    private val vdc: TestRiskLevelCalculationFragmentVDC by vdcsAssisted(
        { vdcSource },
        { factory, handle ->
            factory as TestRiskLevelCalculationFragmentVDC.Factory
            factory.create(handle, navArgs.exampleArgument)
        }
    )
  • vdcSource is a factory that has access to the lookup map we created in step one.
  • vdcsAssisted is a little indirection that allows us to have an injected class with manual arguments, meaning we can have both injected parameters from our DI graph, and manual parameters, such as fragment/nav graph arguments.

Finally we need the VDC (ViewModel) itself:

class TestRiskLevelCalculationFragmentVDC @AssistedInject constructor(
    @Assisted private val handle: SavedStateHandle,
    @Assisted private val exampleArg: String?,
    private val context: Context, // App context injected
) : VDC() {

    init {
        Timber.d("VDC: %s", this)
        Timber.d("SavedStateHandle: %s", handle)
        Timber.d("Example arg: %s", exampleArg)
    }

    @AssistedInject.Factory
    interface Factory : VDCFactory<TestRiskLevelCalculationFragmentVDC> {
        fun create(
            handle: SavedStateHandle,
            exampleArg: String?
        ): TestRiskLevelCalculationFragmentVDC
    }
}
  • Note the little differences to normal Dagger DI, i.e. AssistedInject,and Assisted. Further details: https://github.com/square/AssistedInject
  • SavedStateHandle is a framework class that offers behavior similar to onSavedInstance.

Now use the vdc/viewmodel in your fragment by moving logic into it, and keeping UI updates out of it, i.e.

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        binding.buttonResetRiskLevel.setOnClickListener { vdc.resetRiskLevel() }
        vdc.riskLevelResetEvent.observe2(this) {
            Toast.makeText(
                requireContext(), "Reset done, please fetch diagnosis keys from server again",
                Toast.LENGTH_SHORT
            ).show()
        }
    }

How to test

  • Launch the app and use the TestRiskLevelCalculationFragment
  • Review the code

@d4rken d4rken added enhancement Improvement of an existing feature maintainers Tag pull requests created by maintainers labels Sep 14, 2020
@d4rken d4rken requested a review from chris-cwa September 14, 2020 09:12
@d4rken d4rken requested a review from a team September 14, 2020 14:54
# Conflicts:
#	Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RetrieveDiagnosisKeysTransactionTest.kt
# Conflicts:
#	Corona-Warn-App/build.gradle
#	Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/TestRiskLevelCalculationFragment.kt
Copy link
Contributor

@jakobmoellerdev jakobmoellerdev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor Remarks in the code. Overall I think we should evaluate whether the gain of using pure constructor-based DI is worth the overhead instead of using lateinit vars.

Waiting for more reviews to give approval 👍

@jakobmoellerdev jakobmoellerdev changed the base branch from dev to release/1.4.x September 16, 2020 10:23
@d4rken d4rken added the 1.5.0 label Sep 16, 2020
@d4rken d4rken changed the base branch from release/1.4.x to release/1.5.x September 16, 2020 14:06
@d4rken d4rken requested a review from ralfgehrer September 17, 2020 07:29
@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities (and Security Hotspot 0 Security Hotspots to review)
Code Smell A 0 Code Smells

0.6% 0.6% Coverage
0.0% 0.0% Duplication

@d4rken d4rken merged commit e8320fd into release/1.5.x Sep 17, 2020
@d4rken d4rken deleted the feature/viewmodel-injection branch September 17, 2020 15:12
@harambasicluka harambasicluka added this to the 1.5.0 milestone Oct 6, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

enhancement Improvement of an existing feature maintainers Tag pull requests created by maintainers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants