Heavily expand Dagger post

Signed-off-by: Harsh Shandilya <msfjarvis@gmail.com>
This commit is contained in:
Harsh Shandilya 2020-01-05 16:53:04 +05:30
parent 9546456e66
commit deeaea4511
2 changed files with 64 additions and 45 deletions

View File

@ -1,45 +0,0 @@
+++
categories = []
date = 2019-12-29T21:19:25+05:30
draft = true
slug = "my-dagger-lessons"
tags = []
title = "My Dagger lessons"
+++
[Dagger] is infamous for very good reasons. It's complicated to use, the documentation is an absolute shitshow, and simpler 'alternatives' exist. While [Koin] and to a lesser extent [Kodein] do the job, they're still service locators at their core and don't automatically inject dependencies like Dagger does.
## Background
Before I start, some introductions. I'm Harsh, the solo developer of [Viscerion](https://play.google.com/store/apps/details?id=me.msfjarvis.viscerion), an Android client for [WireGuard](https://www.wireguard.com/). It is a fully [open source](https://github.com/msfjarvis/viscerion) project just like the upstream Android client which served as the base for it. I forked Viscerion as a playground project that'd let me learn new Android tech in an unencumbered way and be something that I'd actually use, hence guaranteeing some level of sustained interest. I do still contribute major fixes back upstream, so put down your pitchforks :P
Like I said before, Viscerion is a learning endeavour, so I decided to learn. I rewrote most of the app in Kotlin, implemented some UI changes to make the app more friendlier to humans and added a couple features here and there.
And then I decided to tackle the Dependency Injection monster.
## Step 1: "Ooh that looks good!"
The dependency injection story always begins with the search for a library that does it for you. You look at Dagger, go to the documentation, have to look up what a Thermosiphon is, and then you scratch Dagger off the list and move on. Kotlin users will end up on one of [Koin] or [Kodein] and that'll be the end of their story.
[Dagger]: https://dagger.dev
[Koin]: http://insert-koin.io/
[Kodein]: https://kodein.org/di/
<!--
What I know now that I didn't before.
1. `@Inject` and `@Provides` is an either-or situation
2. Injecting into a Kotlin `object` seems to be a problematic affair
3. The initial boiler plate is still really, really annoying.
Takeaways from this effort:
- Dagger is ridiculously complex without a human to guide you around.
- Koin's service locator pattern makes it far too easy to write bad code because you can inject anything anywhere.
- Dagger can feel like a lot of boilerplate but some clever techniques can mitigate that.
- Writing `inject` methods for every single class can feel like a drag because it is.
- Injecting into Kotlin `object`s appears to be a no-go. I opted to [refactor out the staticity where possible](https://github.com/msfjarvis/viscerion/pull/214/commits/9eb532521f51d0f7bb66a2a78aa1fc5688128a22), [pass injected dependencies to the function](https://github.com/msfjarvis/viscerion/commit/e23f878140d4bda9e2c54d6c2684e07994066fd6#diff-28007a5799b03e7b556f5bb942754031) or [fall back to 'dirty' patterns](https://github.com/msfjarvis/viscerion/pull/214/commits/fc54ec6bb8e99ec639c6617765e814e12d91ea1a#diff-74f75ab44e1cd2909c4ec4d704bbbab7R65) as needed. Do what you feel like.
- I still do not _love_ Dagger. Fuck you Google.
-->

View File

@ -0,0 +1,64 @@
+++
categories = []
date = 2019-12-29T21:19:25+05:30
draft = true
slug = "my-dagger-story"
tags = ["android", "dagger"]
title = "My Dagger Story"
description = "Dagger is not the easiest tool to get on board with but it's almost worth the effort. Here's the story of my journey to not hating Dagger."
+++
[Dagger] is infamous for very good reasons. It's complicated to use, the documentation is an absolute shitshow, and simpler 'alternatives' exist. While [Koin] and to a lesser extent [Kodein] do the job, they're still service locators at their core and don't automatically inject dependencies like Dagger does.
## Background
Before I start, some introductions. I'm the sole developer of [Viscerion](https://play.google.com/store/apps/details?id=me.msfjarvis.viscerion), an Android client for [WireGuard](https://www.wireguard.com/). It is a fully [open source](https://github.com/msfjarvis/viscerion) project just like the upstream Android client which served as the base for it. I forked Viscerion as a playground project that'd let me learn new Android tech in an unencumbered way and be something that I'd actually use, hence guaranteeing some level of sustained interest. I do still contribute major fixes back upstream, so put down your pitchforks :P
Like I said before, Viscerion is a learning endeavour, so I decided to learn. I rewrote most of the app in Kotlin, implemented some UI changes to make the app more friendlier to humans and added a couple features here and there.
And then I decided to tackle the Dependency Injection monster.
## The beginnings
The dependency injection story always begins with the search for a library that does it for you. You look at Dagger, go to the documentation, look up what a Thermosiphon is, then scratch Dagger off the list and move on. Kotlin users will then end up on one of [Koin] or [Kodein] and that'll be the end of their story.
That was mine as well! Before Viscerion was forked, the app used to have Dagger (albeit sorely underused as I can tell now that I know a fair bit about it) but then it was [swapped out](https://github.com/WireGuard/wireguard-android/commit/712b6c6f600ef6eb683d356a6e9a05e9415b7e12) for singleton access from the Application class. I really, really wanted to try out the fancy 'Dependency Injection' thing everybody loved so I did some searching around, went through the aforementioned motions and [settled on Koin](https://github.com/msfjarvis/viscerion/pull/131).
It was great! I could get any dependency anywhere and that allowed me to write all kinds of hot garbage, and garbage I wrote. But despite all that, I still really wanted to give Dagger another shot. I tried multiple times to make it work, the remains of which have been force pushed away long since. Then I came across [Fred Porciúncula](https://twitter.com/tfcporciuncula)'s [dagger-journey](https://github.com/tfcporciuncula/dagger-journey) repository and the accompanying talk that tried to fill the usability gap that Dagger's documentation never could. He was largely successful in being able to teach me how to use Dagger, and the first "proper" attempt I made at using Dagger was [largely decent](https://github.com/msfjarvis/viscerion/pull/196/files). I was still missing a lot of knowledge that made me [slip back into my hate-train](https://github.com/msfjarvis/viscerion/pull/196#issuecomment-557907972).
## The turning point
Around mid-December 2019, [Arun](https://twitter.com/arunkumar_9t2) released the 0.1 version of his Dagger 2 dependency graph visualizer, [Scabbard](https://arunkumar.dev/introducing-scabbard-a-tool-to-visualize-dagger-2-dependency-graphs/). It looked **awesome**. I [retweeted it](https://twitter.com/MSF_Jarvis/status/1210856668310863872) and shoved in my residual Dagger hate for good measure because isn't that what the internet is for. I was [confident](https://twitter.com/MSF_Jarvis/status/1210866037056397312) that Dagger shall never find a place in my code and my friend [Sasikanth](https://twitter.com/its_sasikanth) (super smart dude, definitely worth following) was [hell-bent on ensuring otherwise](https://twitter.com/MSF_Jarvis/status/1210935581288517632?s=20).
Together, we dug up my previous efforts and I started [a PR](https://github.com/msfjarvis/viscerion/pull/214) so he could review it and help me past the point I dropped out last time. He helped [me on GitHub](https://github.com/msfjarvis/viscerion/pull/214#pullrequestreview-336919368), privately on Telegram and together in about 2 days Viscerion was completely Koin-free and ready to kill. I put down my thoughts about the migration briefly [on the PR](https://github.com/msfjarvis/viscerion/pull/214#issuecomment-569541678), which I'll reproduce and expand on below.
>- Dagger is ridiculously complex without a human to guide you around.
I will die on this hill. Without Sasikanth's help I would have never gotten around to even *trying* Dagger again.
>- Koin's service locator pattern makes it far too easy to write bad code because you can inject anything anywhere.
Again, very strong opinion that I will continue to have. Being able to inject lifecycle sensitive dependencies like `Context` to an `object` felt wrong and still does.
>- Dagger can feel like a lot of boilerplate but some clever techniques can mitigate that.
Because the Dagger documentation wasn't helpful, I didn't realise that a `Provides` annotated method and an `@Inject`ed constructor was an either-or situation and I didn't need to write both for a class to be injectable. Sasikanth [with the rescue again](https://github.com/msfjarvis/viscerion/pull/214#discussion_r361800427).
>- Writing `inject` methods for every single class can feel like a drag because it is.
[I mean...](https://github.com/msfjarvis/viscerion/blob/master/app/src/main/java/com/wireguard/android/di/AppComponent.kt#L69-L101)
>- Injecting into Kotlin `object`s appears to be a no-go. I opted to [refactor out the staticity where possible](https://github.com/msfjarvis/viscerion/pull/214/commits/9eb532521f51d0f7bb66a2a78aa1fc5688128a22), [pass injected dependencies to the function](https://github.com/msfjarvis/viscerion/commit/e23f878140d4bda9e2c54d6c2684e07994066fd6#diff-28007a5799b03e7b556f5bb942754031) or [fall back to 'dirty' patterns](https://github.com/msfjarvis/viscerion/pull/214/commits/fc54ec6bb8e99ec639c6617765e814e12d91ea1a#diff-74f75ab44e1cd2909c4ec4d704bbbab7R65) as needed. Do what you feel like.
I have no idea if that's even a good ability to begin with, so I chose to change myself rather than fight the system.
>- I still do not _love_ Dagger. Fuck you Google.
This, I probably don't subscribe to anymore. Dagger was horrible to get on board, but I am now in a position where I can find someone who's stuck like I was and help them come over the same way my friends helped me.
[Dagger]: https://dagger.dev
[Koin]: http://insert-koin.io/
[Kodein]: https://kodein.org/di/