Slide 1

Slide 1 text

@HandstandSam #DevFestFL DIY Dependency Injection with Kotlin Sam Edwards -

Slide 2

Slide 2 text

@HandstandSam #DevFestFL Why am I talking about DIY Dependency Injection with Kotlin?

Slide 3

Slide 3 text

@HandstandSam #DevFestFL Why am I talking about DIY Dependency Injection with Kotlin? ! Share the story of how we implemented DIY

Slide 4

Slide 4 text

@HandstandSam #DevFestFL Why am I talking about DIY Dependency Injection with Kotlin? ! Share the story of how we implemented DIY ! Demystify the topic of Dependency Injection

Slide 5

Slide 5 text

@HandstandSam #DevFestFL Why am I talking about DIY Dependency Injection with Kotlin? ! Share the story of how we implemented DIY ! Demystify the topic of Dependency Injection ! Show how you can implement DIY, if you choose

Slide 6

Slide 6 text

@HandstandSam #DevFestFL Our Story

Slide 7

Slide 7 text

@HandstandSam #DevFestFL Our Story ! Our team previously built Capital One Wallet

Slide 8

Slide 8 text

@HandstandSam #DevFestFL Our Story ! Our team previously built Capital One Wallet ! We got tasked to build a reusable SDK for Authentication

Slide 9

Slide 9 text

@HandstandSam #DevFestFL Our Story ! Our team previously built Capital One Wallet ! We got tasked to build a reusable SDK for Authentication ! This was the first time we were building a complex SDK

Slide 10

Slide 10 text

@HandstandSam #DevFestFL Product Requirements

Slide 11

Slide 11 text

@HandstandSam #DevFestFL Product Requirements ! SDK will be used by different apps, in different codebases

Slide 12

Slide 12 text

@HandstandSam #DevFestFL Product Requirements ! SDK will be used by different apps, in different codebases ! Should not assume clients will be using a 3rd party library

Slide 13

Slide 13 text

@HandstandSam #DevFestFL Product Requirements ! SDK will be used by different apps, in different codebases ! Should not assume clients will be using a 3rd party library ! Stay lean and lightweight

Slide 14

Slide 14 text

@HandstandSam #DevFestFL Product Requirements ! SDK will be used by different apps, in different codebases ! Should not assume clients will be using a 3rd party library ! Stay lean and lightweight ! Make the SDK configurable

Slide 15

Slide 15 text

@HandstandSam #DevFestFL Technical Requirements

Slide 16

Slide 16 text

@HandstandSam #DevFestFL Technical Requirements ! Reactive Programming

Slide 17

Slide 17 text

@HandstandSam #DevFestFL Technical Requirements ! Reactive Programming ! Immutable Data

Slide 18

Slide 18 text

@HandstandSam #DevFestFL Technical Requirements ! Reactive Programming ! Immutable Data ! Modular Code

Slide 19

Slide 19 text

@HandstandSam #DevFestFL Technical Requirements ! Reactive Programming ! Immutable Data ! Modular Code ! Single Responsibility Code

Slide 20

Slide 20 text

@HandstandSam #DevFestFL Technical Requirements ! Reactive Programming ! Immutable Data ! Modular Code ! Single Responsibility Code ! Well Tested Code

Slide 21

Slide 21 text

@HandstandSam #DevFestFL Technical Requirements ! Reactive Programming ! Immutable Data ! Modular Code ! Single Responsibility Code ! Well Tested Code ! Avoid 3rd Party Libraries

Slide 22

Slide 22 text

@HandstandSam #DevFestFL Technical Requirements ! Reactive Programming ! Immutable Data ! Modular Code ! Single Responsibility Code ! Well Tested Code ! Avoid 3rd Party Libraries ! 100% Kotlin

Slide 23

Slide 23 text

@HandstandSam #DevFestFL Technical Requirements ! Reactive Programming ! Immutable Data ! Modular Code ! Single Responsibility Code ! Well Tested Code ! Avoid 3rd Party Libraries ! 100% Kotlin ! Dependency Injection

Slide 24

Slide 24 text

@HandstandSam #DevFestFL Why 100% Kotlin?

Slide 25

Slide 25 text

@HandstandSam #DevFestFL Why 100% Kotlin? makes coding easy and concise.

Slide 26

Slide 26 text

@HandstandSam #DevFestFL Why 100% Kotlin? makes dependency injection easy and concise.

Slide 27

Slide 27 text

@HandstandSam #DevFestFL Why Dependency Injection?

Slide 28

Slide 28 text

@HandstandSam #DevFestFL Why Dependency Injection? ! Testing

Slide 29

Slide 29 text

@HandstandSam #DevFestFL Why Dependency Injection? ! Testing ! Mock Flavors

Slide 30

Slide 30 text

@HandstandSam #DevFestFL Why Dependency Injection? ! Testing ! Mock Flavors ! Custom Debug Features

Slide 31

Slide 31 text

@HandstandSam #DevFestFL Why Dependency Injection? ! Testing ! Mock Flavors ! Custom Debug Features ! Environment Switching (Test vs Production Servers)

Slide 32

Slide 32 text

@HandstandSam #DevFestFL Why Dependency Injection? ! Testing ! Mock Flavors ! Custom Debug Features ! Environment Switching (Test vs Production Servers) ! Promotes building more reusable code

Slide 33

Slide 33 text

@HandstandSam #DevFestFL Why Dependency Injection? ! Testing ! Mock Flavors ! Custom Debug Features ! Environment Switching (Test vs Production Servers) ! Promotes building more reusable code ! Discourages use of static singletons

Slide 34

Slide 34 text

@HandstandSam #DevFestFL Why Dependency Injection? ! Testing ! Mock Flavors ! Custom Debug Features ! Environment Switching (Test vs Production Servers) ! Promotes building more reusable code ! Discourages use of static singletons ! It enables all the really cool things I like to talk about

Slide 35

Slide 35 text

@HandstandSam #DevFestFL It Enables Testing DevFest Florida

Slide 36

Slide 36 text

@HandstandSam #DevFestFL It Enables Custom Debug Features Droidcon Boston

Slide 37

Slide 37 text

@HandstandSam #DevFestFL It Enable Mock Flavors Droidcon NYC

Slide 38

Slide 38 text

@HandstandSam #DevFestFL Dependency Injection is Not Magical

Slide 39

Slide 39 text

@HandstandSam #DevFestFL Dependency Injection is Not Magical DI !=

Slide 40

Slide 40 text

@HandstandSam #DevFestFL Dependency Injection is

Slide 41

Slide 41 text

@HandstandSam #DevFestFL Dependency Injection is Inversion of Control

Slide 42

Slide 42 text

@HandstandSam #DevFestFL Dependency Injection is Passing in dependencies
 instead of pre-defining them. Inversion of Control

Slide 43

Slide 43 text

@HandstandSam #DevFestFL Dependency Injection is Inversion of Control Code execute Dependencies ( )

Slide 44

Slide 44 text

@HandstandSam #DevFestFL Dependency Injection is Inversion of Control Code execute ( ) Dependencies

Slide 45

Slide 45 text

@HandstandSam #DevFestFL Without Dependency Injection class NetworkConfig( val fullUrl = "http://localhost:8080/" val isLocalhostServer = true }

Slide 46

Slide 46 text

@HandstandSam #DevFestFL Setter Injection class NetworkConfig( var fullUrl: String, var isLocalhostServer: Boolean ) val config = NetworkConfig() config.fullUrl = “http://localhost:8080/" config.isLocalhostServer = true

Slide 47

Slide 47 text

@HandstandSam #DevFestFL Constructor Injection class NetworkConfig( val baseUrl: String, val port: Int ) { val fullUrl = "$baseUrl:$port/" val isLocalhostServer = baseUrl.contains("//localhost") }

Slide 48

Slide 48 text

@HandstandSam #DevFestFL Commonalities: Inversion of Control ! Inversion of Control is passing in dependencies instead of pre- defining them.

Slide 49

Slide 49 text

@HandstandSam #DevFestFL Aren’t Their Libraries That Do DI for You? ! Yes, let’s take a look.

Slide 50

Slide 50 text

@HandstandSam #DevFestFL Open Source DI Libraries ! Dagger ! Toothpick ! Koin ! Kodein

Slide 51

Slide 51 text

@HandstandSam #DevFestFL ! Dagger ! Toothpick ! Koin ! Kodein Code Generation YES YES NO NO

Slide 52

Slide 52 text

@HandstandSam #DevFestFL ! Dagger ! Toothpick ! Koin ! Kodein Code Generation YES YES NO NO ❌ ❌

Slide 53

Slide 53 text

@HandstandSam #DevFestFL ! Dagger ! Toothpick ! Koin ! Kodein Code Generation YES YES NO NO Single
 Instance NO NO YES NO ❌ ❌

Slide 54

Slide 54 text

@HandstandSam #DevFestFL ! Dagger ! Toothpick ! Koin ! Kodein Code Generation YES YES NO NO Single
 Instance NO NO YES NO ❌ ❌ ❌

Slide 55

Slide 55 text

@HandstandSam #DevFestFL ! Dagger ! Toothpick ! Koin ! Kodein Code Generation YES YES NO NO Single
 Instance NO NO YES NO Available
 at the time YES YES YES NO ❌ ❌ ❌

Slide 56

Slide 56 text

@HandstandSam #DevFestFL ! Dagger ! Toothpick ! Koin ! Kodein Code Generation YES YES NO NO Single
 Instance NO NO YES NO Available
 at the time YES YES YES NO ❌ ❌ ❌ ❌

Slide 57

Slide 57 text

@HandstandSam #DevFestFL ! Dagger ! Toothpick ! Koin ! Kodein Code Generation YES YES NO NO Single
 Instance NO NO YES NO Available
 at the time YES YES YES NO Adds dependency on another library YES YES YES YES ❌ ❌ ❌ ❌

Slide 58

Slide 58 text

@HandstandSam #DevFestFL So, How Much Work is DIY?

Slide 59

Slide 59 text

@HandstandSam #DevFestFL So, How Much Work is DIY? ! Not that much

Slide 60

Slide 60 text

@HandstandSam #DevFestFL So, How Much Work is DIY? ! Not that much ! Slightly more verbose

Slide 61

Slide 61 text

@HandstandSam #DevFestFL So, How Much Work is DIY? ! Not that much ! Slightly more verbose ! Have to manually build advanced DI features

Slide 62

Slide 62 text

@HandstandSam #DevFestFL So, How Much Work is DIY? ! Not that much ! Slightly more verbose ! Have to manually build advanced DI features ! Great for simple use cases when you don’t already know a DI library really well

Slide 63

Slide 63 text

@HandstandSam #DevFestFL ShoppingApp - Open Source on GitHub https:/ /github.com/handstandsam/ShoppingApp

Slide 64

Slide 64 text

@HandstandSam #DevFestFL Swapping Dependencies on a Graph

Slide 65

Slide 65 text

@HandstandSam #DevFestFL Swapping Dependencies on a Graph ! Mock Server vs. Testing Server vs. Live (Production Server)

Slide 66

Slide 66 text

@HandstandSam #DevFestFL Swapping Dependencies on a Graph ! Mock Server vs. Testing Server vs. Live (Production Server) ! Graph is where all your dependencies are configured.

Slide 67

Slide 67 text

@HandstandSam #DevFestFL Swapping Dependencies on a Graph ! Mock Server vs. Testing Server vs. Live (Production Server) ! Graph is where all your dependencies are configured. ! Just swap out pieces of your graph you need to change.

Slide 68

Slide 68 text

@HandstandSam #DevFestFL Swapping Dependencies on a Graph ! Mock Server vs. Testing Server vs. Live (Production Server) ! Graph is where all your dependencies are configured. ! Just swap out pieces of your graph you need to change. ! Or provide a brand new graph with the required fields.

Slide 69

Slide 69 text

@HandstandSam #DevFestFL Network Graph interface NetworkGraph { val categoryRepo: CategoryRepo val itemRepo: ItemRepo val userRepo: UserRepo }

Slide 70

Slide 70 text

@HandstandSam #DevFestFL Network Graph interface NetworkGraph { val categoryRepo: CategoryRepo val itemRepo: ItemRepo val userRepo: UserRepo } class NetworkGraphImpl( networkConfig: NetworkConfig ) : NetworkGraph { private val retrofit: Retrofit= Retrofit.Builder() .baseUrl(networkConfig.fullUrl) .addConverterFactory(moshiConverterFactory) .addCallAdapterFactory(rxJava2CallAdapterFactory) .client(okHttpClientBuilder.build()) .build() private val shoppingService: ShoppingService = retrofit.create(ShoppingService::class.java) override val categoryRepo: CategoryRepo = NetworkCategoryRepo(shoppingService) override val itemRepo: ItemRepo = NetworkItemRepo(shoppingService) override val userRepo: UserRepo = NetworkUserRepo(shoppingService) }

Slide 71

Slide 71 text

@HandstandSam #DevFestFL Network Graph interface NetworkGraph { val categoryRepo: CategoryRepo val itemRepo: ItemRepo val userRepo: UserRepo } class NetworkGraphImpl( networkConfig: NetworkConfig ) : NetworkGraph { private val retrofit: Retrofit= Retrofit.Builder() .baseUrl(networkConfig.fullUrl) .addConverterFactory(moshiConverterFactory) .addCallAdapterFactory(rxJava2CallAdapterFactory) .client(okHttpClientBuilder.build()) .build() private val shoppingService: ShoppingService = retrofit.create(ShoppingService::class.java) override val categoryRepo: CategoryRepo = NetworkCategoryRepo(shoppingService) override val itemRepo: ItemRepo = NetworkItemRepo(shoppingService) override val userRepo: UserRepo = NetworkUserRepo(shoppingService) }

Slide 72

Slide 72 text

@HandstandSam #DevFestFL Create Graphs of Dependencies • Similar to Dagger @Component OkHttpClient Services Retrofit NetworkGraph Url Repos

Slide 73

Slide 73 text

@HandstandSam #DevFestFL Session Graph interface SessionGraph { val sessionManager: SessionManager val checkoutCart: CheckoutCart val userPreferences: UserPreferences }

Slide 74

Slide 74 text

@HandstandSam #DevFestFL Session Graph interface SessionGraph { val sessionManager: SessionManager val checkoutCart: CheckoutCart val userPreferences: UserPreferences } class SessionGraphImpl( appContext: Context ) : SessionGraph { override val checkoutCart = CheckoutCart() override val userPreferences = UserPreferences(appContext) override val sessionManager = SessionManager(checkoutCart, userPreferences) }

Slide 75

Slide 75 text

@HandstandSam #DevFestFL Multiple or Nested Graphs are AppGraph SessionGraph NetworkGraph

Slide 76

Slide 76 text

@HandstandSam #DevFestFL Multiple or Nested Graphs are AppGraph SessionGraph NetworkGraph

Slide 77

Slide 77 text

@HandstandSam #DevFestFL Multiple or Nested Graphs are AppGraph SessionGraph NetworkGraph ! Each module project needs their own graph

Slide 78

Slide 78 text

@HandstandSam #DevFestFL Multiple or Nested Graphs are AppGraph SessionGraph NetworkGraph ! Each module project needs their own graph ! Each graph is similar to a Dagger @Component

Slide 79

Slide 79 text

@HandstandSam #DevFestFL ! Use the `by lazy` language feature of Kotlin to delay initialization, but cache result. Leveraging `by lazy`

Slide 80

Slide 80 text

@HandstandSam #DevFestFL ! Use the `by lazy` language feature of Kotlin to delay initialization, but cache result. Leveraging `by lazy` class AppGraph { val networkGraph by lazy NetworkGraph(config = config) }

Slide 81

Slide 81 text

@HandstandSam #DevFestFL ! Create a factory with a lambda Factories

Slide 82

Slide 82 text

@HandstandSam #DevFestFL ! Create a factory with a lambda Factories class AppGraph { val dbFactory: () -> Database = {Database(logger = logger, utils = utils)} } val db = dbFactory()

Slide 83

Slide 83 text

@HandstandSam #DevFestFL Scoping

Slide 84

Slide 84 text

@HandstandSam #DevFestFL ! Use a factory, but this is DIY, so you will have to manage scoping yourself. Scoping

Slide 85

Slide 85 text

@HandstandSam #DevFestFL Setting up Your Graphs class MyApplication : Application { val graph by lazy { AppGraph(this) } override fun onCreate() { //Access Graph to Instantiate graph } } ! Create Graph during Application::onCreate() and attach to Application

Slide 86

Slide 86 text

@HandstandSam #DevFestFL Setting up Your Graphs class MyApplication : Application { val graph by lazy { AppGraph(this) } override fun onCreate() { //Access Graph to Instantiate graph } } class MyApplication : Application { lateinit var graph: AppGraph override fun onCreate() { graph = AppGraph(this) } } or ! Create Graph during Application::onCreate() and attach to Application

Slide 87

Slide 87 text

@HandstandSam #DevFestFL Supporting Graphs in Modules class AppGraph(context: Context) { init { instance = this } companion object { lateinit var instance: AppGraph } }

Slide 88

Slide 88 text

@HandstandSam #DevFestFL ! Create a singleton since the module is unaware of the Application Supporting Graphs in Modules class AppGraph(context: Context) { init { instance = this } companion object { lateinit var instance: AppGraph } }

Slide 89

Slide 89 text

@HandstandSam #DevFestFL Dependency Injection on Activities & Fragments

Slide 90

Slide 90 text

@HandstandSam #DevFestFL ! Constructors of Application, Fragments, Activities & Services are called by the platform Dependency Injection on Activities & Fragments

Slide 91

Slide 91 text

@HandstandSam #DevFestFL ! Constructors of Application, Fragments, Activities & Services are called by the platform ! You have to wait do do any injection until after the `onCreate` lifecycle method Dependency Injection on Activities & Fragments

Slide 92

Slide 92 text

@HandstandSam #DevFestFL ! lateinit var, grabbing from Application Accessing Graph in Activities & Fragments class MyActivity : Activity() { lateinit private var graph: AppGraph override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) graph = (application as MyApplication).graph //Access your fields now graph.database } }

Slide 93

Slide 93 text

@HandstandSam #DevFestFL ! Simplify using an extension function. Accessing Graph in Activities & Fragments class MyActivity : Activity() { lateinit private var graph: AppGraph override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) graph = application.graph } } fun Application.graph(): AppGraph{ return (this as MyApplication).graph }

Slide 94

Slide 94 text

@HandstandSam #DevFestFL ! OR use `by lazy` to defer access until the Activity has been created Accessing Graph in Activities & Fragments class MyActivity : Activity() { private val graph: AppGraph get() = application.graph() private val db: UserRepo by lazy {graph.networkGraph.userRepo} } fun Application.graph(): AppGraph{ return (this as MyApplication).graph }

Slide 95

Slide 95 text

@HandstandSam #DevFestFL Accessing Graph in Activities & Fragments class MyActivity : Activity() { private val graph: AppGraph get() = application.graph() private val db: UserRepo get() = graph.networkGraph.userRepo } fun Application.graph(): AppGraph{ return (this as MyApplication).graph }

Slide 96

Slide 96 text

@HandstandSam #DevFestFL ! OR use `get() =` to defer access until the Activity has been created Accessing Graph in Activities & Fragments class MyActivity : Activity() { private val graph: AppGraph get() = application.graph() private val db: UserRepo get() = graph.networkGraph.userRepo } fun Application.graph(): AppGraph{ return (this as MyApplication).graph }

Slide 97

Slide 97 text

@HandstandSam #DevFestFL Pro Tips

Slide 98

Slide 98 text

@HandstandSam #DevFestFL Pro Tips ! Try and avoid `lateinit var` for injected values

Slide 99

Slide 99 text

@HandstandSam #DevFestFL Pro Tips ! Try and avoid `lateinit var` for injected values ! `lateinit` can create runtime Exceptions

Slide 100

Slide 100 text

@HandstandSam #DevFestFL Pro Tips ! Try and avoid `lateinit var` for injected values ! `lateinit` can create runtime Exceptions ! `var` creates mutable state

Slide 101

Slide 101 text

@HandstandSam #DevFestFL Pro Tips ! Try and avoid `lateinit var` for injected values ! `lateinit` can create runtime Exceptions ! `var` creates mutable state ! `by lazy` create an anonymous class

Slide 102

Slide 102 text

@HandstandSam #DevFestFL Pro Tips ! Try and avoid `lateinit var` for injected values ! `lateinit` can create runtime Exceptions ! `var` creates mutable state ! `by lazy` create an anonymous class ! You can use `get() =` if your data is an immutable singleton

Slide 103

Slide 103 text

@HandstandSam #DevFestFL Accessing Dependencies from the Graph LoginPresenter( view = loginView, sessionManager = appGraph.sessionGraph.sessionManager, userRepo = appGraph.networkGraph.userRepo )

Slide 104

Slide 104 text

@HandstandSam #DevFestFL Accessing Dependencies from the Graph private val sessionManager: SessionManager get() = graph.sessionGraph.sessionManager private val userRepo: UserRepo get() = graph.networkGraph.userRepo LoginPresenter( view = loginView, sessionManager = sessionManager, userRepo = userRepo )

Slide 105

Slide 105 text

@HandstandSam #DevFestFL Key Takeaways

Slide 106

Slide 106 text

@HandstandSam #DevFestFL Key Takeaways ! You don’t HAVE to use a Dependency Injection Library, Dagger

Slide 107

Slide 107 text

@HandstandSam #DevFestFL Key Takeaways ! You don’t HAVE to use a Dependency Injection Library, Dagger ! There are no magic annotations. @Inject

Slide 108

Slide 108 text

@HandstandSam #DevFestFL Key Takeaways ! You don’t HAVE to use a Dependency Injection Library, Dagger ! There are no magic annotations. @Inject ! Kotlin language features make this concise and straightforward

Slide 109

Slide 109 text

@HandstandSam #DevFestFL Key Takeaways ! You don’t HAVE to use a Dependency Injection Library, Dagger ! There are no magic annotations. @Inject ! Kotlin language features make this concise and straightforward ! Dependency Injection is critical for any app

Slide 110

Slide 110 text

@HandstandSam #DevFestFL Thank you. My team is hiring in DC, Richmond & San Francisco.