Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Strategies for Migrating to Jetpack Compose

Mohit S
April 19, 2022

Strategies for Migrating to Jetpack Compose

Mohit S

April 19, 2022
Tweet

More Decks by Mohit S

Other Decks in Programming

Transcript

  1. Mohit Sarveiya
    Strategies for Migrating to Jetpack Compose
    @heyitsmohit

    View Slide

  2. Strategies for Migrating to Jetpack Compose
    ● Challenges

    ● How to think about migration

    ● Strategies & Tools

    View Slide

  3. Challenges

    View Slide

  4. Code size
    Code

    size
    Time

    View Slide

  5. Greenfield Project
    ● Easier to use Compose

    ● Fewer roadblocks to use Compose

    View Slide

  6. Code size
    Code

    size
    Time

    View Slide

  7. Roadmap
    0% Compose
    100% Compose

    View Slide

  8. Roadmap
    0% Compose
    100% Compose
    Roadblock
    Roadblock

    View Slide

  9. Roadmap
    ● Long road

    ● Obstacles along the way

    View Slide

  10. Arch Fragmentation
    Codebase

    View Slide

  11. Arch Fragmentation
    Codebase
    Legacy code

    View Slide

  12. Arch Fragmentation
    Codebase
    Legacy code
    Feature A
    Feature B

    View Slide

  13. Challenges
    ● Interoperability

    View Slide

  14. Interop
    ● Compose to old view system

    View Slide

  15. Interop
    Codebase
    Feature A
    Compose View
    Legacy code

    View Slide

  16. Interop
    Codebase
    Legacy code
    Feature A
    Compose View

    View Slide

  17. Interop
    ● Compose to old view system

    ● Use old views in Compose

    View Slide

  18. Interop
    Codebase
    Legacy code
    Feature A
    Custom View

    View Slide

  19. Interop
    Codebase
    Feature A
    Compose with

    Custom View
    Legacy code

    View Slide

  20. Challenges
    ● Interoperability

    ● Architecture

    View Slide

  21. Architecture
    Codebase
    Legacy code

    View Slide

  22. Arch Challenges
    ● Not using composition over inheritance

    View Slide

  23. Legacy Code
    Base Fragment Too much logic

    View Slide

  24. Legacy Code
    Base Fragment Too much logic
    Fragment Fragment Fragment

    View Slide

  25. Legacy Code
    Base Fragment
    Fragment Fragment Fragment

    Refactor to Compose

    View Slide

  26. Legacy Code
    Base View
    Holders
    Too much logic

    View Slide

  27. Legacy Code
    Base View
    Holders
    Too much logic
    View Holder View Holder View Holder

    Refactor to Compose

    View Slide

  28. Arch Challenges
    ● Not using composition over inheritance

    ● Navigation

    View Slide

  29. App Architecture
    API
    Domain
    Navigation
    UI

    View Slide

  30. Navigation
    NavHost(navController, startDestination = "profile") {

    composable("profile") {

    Profile(
    /*...*/
    )

    }

    composable("friendslist") {

    FriendsList(
    /*...*/
    )

    }

    }

    View Slide

  31. Legacy Navigation
    Main Activity Activity Activity

    View Slide

  32. Navigation
    Main Activity
    Fragment A Fragment B Fragment C
    ...

    View Slide

  33. Challenges
    ● Interoperability

    ● Architecture

    ● Testing

    View Slide

  34. Testing
    Codebase 1% Code coverage
    5% Code coverage

    View Slide

  35. Challenges
    ● Interoperability

    ● Architecture

    ● Testing

    ● Tooling

    View Slide

  36. Compose Upgrades
    Version
    Time
    alpha05
    alpha06

    View Slide

  37. Challenges
    ● Interoperability

    ● Architecture

    ● Testing

    ● Tooling

    View Slide

  38. Collaboration
    Org
    CI Arch Design Team Feature Teams
    Teams

    View Slide

  39. Strategies

    View Slide

  40. Roadmap
    0% Compose
    100% Compose

    View Slide

  41. Roadmap
    0% Compose
    100% Compose
    Milestone
    Milestone

    View Slide

  42. Proposal
    ● Specify milestones over time

    ● Assess & Mitigate Risks

    ● Documentation

    View Slide

  43. Roadmap
    0% Compose
    100% Compose
    Milestone

    View Slide

  44. Complexity
    Codebase
    High
    Low
    Medium

    View Slide

  45. Migration Phase 1
    Complexity
    Migration time
    Low

    View Slide

  46. Migration Phase 2
    Complexity
    Migration time
    Medium

    View Slide

  47. Migration Phase 1
    ● Migrate Design Components

    View Slide

  48. Example
    Feature A Feature B Feature C
    Common

    Design Components

    View Slide

  49. Example
    Common

    Design Components
    App A App B
    App C

    View Slide

  50. Collaboration
    Org
    CI Arch Design Team Feature Teams
    Teams

    View Slide

  51. Migration Phase 1
    ● Migrate design components to Compose

    View Slide

  52. Interoperability

    View Slide

  53. Interop
    9:41

    View Slide

  54. Interop
    9:41
    Convert to compose

    View Slide

  55. Interop


    />


    />


    />


    />



    FrameLayout>

    9:41

    View Slide

  56. Interop


    />


    />


    />


    />



    FrameLayout>

    9:41

    View Slide

  57. Interop


    />


    />



    android:id="@+id/compose_view"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"
    />



    FrameLayout>

    View Slide

  58. Compose View
    binding.composeView.apply {

    setContent {

    }

    }

    View Slide

  59. Compose View
    binding.composeView.apply {

    setContent {

    MaterialTheme {

    DescriptionView()

    }

    }

    }

    View Slide

  60. Challenges
    strings.xml
    dimen.xml
    theme.xml
    Compose
    Resuse

    View Slide

  61. Compose View
    @Composable

    fun DescriptionView() {

    Text(

    text = stringResource(id = R.string.title)

    )

    }

    View Slide

  62. Compose View
    @Composable

    fun DescriptionView() {

    Text(

    modifier = Modifier.padding(dimensionResource(R.id.margin_small))
    )

    }

    View Slide

  63. Challenges
    ● How do you reuse dimen and string resources?

    ● Interop Theming

    View Slide

  64. Challenges
    strings.xml
    dimen.xml
    theme.xml
    Compose
    Resuse

    View Slide

  65. Interop Theming
    implementation “com.google.android.material:compose-theme-adapter:1.1.1”

    View Slide

  66. Interop Theming
    binding.composeView.apply {

    setContent {

    MaterialTheme {

    DescriptionView()

    }

    }

    }

    View Slide

  67. Interop Theming
    binding.composeView.apply {

    setContent {

    MdcTheme {

    DescriptionView()

    }

    }

    }

    View Slide

  68. Challenges
    ● How do you reuse dimen and string resources?

    ● Interop Theming

    ● View lifecycle

    View Slide

  69. Compose View
    binding.composeView.apply {

    setContent {

    setViewCompositionStrategy(

    ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed

    )

    }

    }

    View Slide

  70. Phase 1 Migration
    Common

    Design Components
    App A App B
    App C

    View Slide

  71. Common Design Components
    ● View all compose components

    ● Document design system

    View Slide

  72. airbnb/Showkase
    Code ! Issues Pull Requests
    Showkase

    Compatible with Compose 1.0.4
    Showcase 1.0.0-beta12
    Showkase is an annotation-processor based Android library that
    helps you organize, discover, search and visualize Jetpack
    Compose UI elements.

    View Slide

  73. Showkase
    9:41
    App
    com.app
    General
    Logs
    Other
    Keyline overlay
    Slow down animations
    Network Activity
    Logs
    Lifecycle logs
    Show UX

    View Slide

  74. Showkase
    9:41
    App
    com.app
    General
    Logs
    Other
    Keyline overlay
    Slow down animations
    Network Activity
    Logs
    Lifecycle logs
    Show UX
    9:41
    Design Components
    Components
    Colors
    Typography

    View Slide

  75. Showkase
    Compose Design Spec
    Generate

    View Slide

  76. Showkase
    @ShowkaseRoot

    class AppRootModule: ShowkaseRootModule

    View Slide

  77. Showkase
    startActivity(Showkase.getBrowserIntent(this))

    View Slide

  78. Showkase
    @Preview(name = “Custom component", group = "Custom group")

    @Composable

    fun PostView()

    View Slide

  79. Showkase
    9:41
    Design Components
    Post view
    Title

    View Slide

  80. Arch Fragmentation
    Codebase
    Legacy code
    100% Compose
    100% Compose

    View Slide

  81. Interop
    @Composable

    fun PostView() {

    AndroidView(factory = { context
    ->






    })

    View Slide

  82. Interop
    @Composable

    fun PostView() {

    AndroidView(factory = { context
    ->


    CustomView(context).apply {

    layoutParams = LinearLayout.LayoutParams(

    MATCH_PARENT,

    WRAP_CONTENT

    )

    }

    })

    View Slide

  83. Interop
    ● ComposeView

    ● AndroidView

    View Slide

  84. Migration Phase 1
    ● Migrate Design Components

    View Slide

  85. Roadmap
    0% Compose
    100% Compose
    Milestone

    View Slide

  86. Roadmap
    0% Compose
    100% Compose
    Milestone
    Milestone

    View Slide

  87. Migration Phase 2
    Complexity
    Migration time
    Medium

    View Slide

  88. Testing
    Codebase 1% Code coverage
    5% Code coverage

    View Slide

  89. Migration Phase 2
    ● Migrate medium complexity features

    ● Setup scaffolding to catch regressions

    View Slide

  90. Testing

    View Slide

  91. Testing Infra
    Compose Conversions
    Testing Infra

    View Slide

  92. Goals
    ● Catch regressions earlier

    ● Increase covers with compose migration

    View Slide

  93. Testing Infra
    ● Snapshot testing

    ● UI Testing Interop

    View Slide

  94. cashapp/paparazzi
    Code ! Issues Pull Requests
    Paparazzi

    An Android library to render your application screens without
    a physical device or emulator.

    View Slide

  95. Paparazzi
    Screenshot
    Unit Test

    View Slide

  96. Paparazzi
    ● 1.0.0-Snapshot has compose support

    View Slide

  97. Paparazzi
    @get:Rule

    val paparazzi = Paparazzi()

    View Slide

  98. Paparazzi
    @get:Rule

    val paparazzi = Paparazzi()

    @Test

    fun testView() {

    paparazzi.snapshot {

    PostView(uiState)

    }

    }

    View Slide

  99. Paparazzi
    9:41
    Post
    Title

    View Slide

  100. Paparazzi
    Git (LFS)
    Snapshot

    View Slide

  101. Testing Infra
    ● Snapshot testing

    ● UI Testing Interop

    View Slide

  102. Interop


    />


    />


    />



    FrameLayout>

    9:41

    View Slide

  103. Interop
    9:41
    Semantics Tree

    View Slide

  104. Compose UI Testing
    @get:Rule

    val composeTestRule = createAndroidComposeRule()

    View Slide

  105. Compose UI Testing
    @Test

    fun postViewTest() {

    composeTestRule.onNodeWithText(“Title").assertIsDisplayed()

    }

    View Slide

  106. Interop
    @Composable

    fun PostView() {

    AndroidView(factory = { context
    ->


    CustomView(context)

    })

    }

    View Slide

  107. Interop
    @Test

    fun postViewTest() {

    composeTestRule.onNodeWithText("Submit").performClick()

    }

    View Slide

  108. Interop
    @Test

    fun postViewTest() {

    composeTestRule.onNodeWithText(“Submit").performClick()

    Espresso.onView(withText(“Success")).check(matches(isDisplayed()))

    }

    View Slide

  109. Migration Phase 2
    ● Migrate medium complexity features

    ● Setup scaffolding to catch regressions

    View Slide

  110. Migration Phases
    0% Compose Milestones

    View Slide

  111. Migration Phases
    ● Milestones depend on complexity & code size

    ● Low Complexity
    ->
    High Complexity

    View Slide

  112. Migration Phases
    0% Compose Milestones
    Testing Infra

    View Slide

  113. Internationalization

    View Slide

  114. Migration Phases
    0% Compose Milestones

    View Slide

  115. Languages Support
    strings.xml
    values-en
    values-de
    values-fr
    strings.xml
    strings.xml

    View Slide

  116. Languages Support
    Add strings

    View Slide

  117. Languages Support
    Translation service
    Add strings

    View Slide

  118. Languages Support
    Translation service
    Provides translated strings
    Add strings

    View Slide

  119. Languages Support
    ● Compose language interop

    View Slide

  120. adrielcafe/lyricist
    Code ! Issues Pull Requests
    Lyricist

    The missing I18N and L10N multiplatform library for Jetpack
    Compose!

    View Slide

  121. Languages Support
    Strings

    (xml)
    Types
    KSP
    Composition

    Local

    View Slide

  122. Languages Support
    strings.xml
    values
    values-pt
    strings.xml

    View Slide

  123. Languages Support


    Hello world

    string>



    Avocado

    item>


    string-array>



    %d zero

    item>


    plurals>


    resources>

    View Slide

  124. Languages Support


    Olá mundo

    string>



    Abacate

    item>


    string-array>



    %d zero

    item>


    plurals>


    resources>

    View Slide

  125. Languages Support
    ksp {

    arg("lyricist.xml.resourcesPath",

    android.sourceSets.main.res.srcDirs.first().absolutePath)

    arg("lyricist.xml.moduleName", “xml")

    arg("lyricist.xml.defaultLanguageTag", "en")

    }

    View Slide

  126. Languages Support
    data class Strings(

    val greeting: String,

    val array: List,

    val plurals: (quantity: Int)
    ->
    String

    )

    object Locales {

    val En = "en"

    val Pt = "pt"

    }

    View Slide

  127. Languages Support
    @Composable

    public fun ProvideStrings() {

    CompositionLocalProvider(

    provider provides lyricist.strings

    )

    }

    View Slide

  128. Languages Support
    @Preview

    @Composable

    fun PostViewPreview() {



    val xmlStrings = rememberXmlStrings()

    ProvideXmlStrings(xmlStrings) {

    Text(text = xmlStrings.greeting)

    }

    }

    View Slide

  129. Languages Support
    @Preview

    @Composable

    fun PostViewPreview() {



    val xmlStrings = rememberXmlStrings()

    ProvideXmlStrings(xmlStrings) {

    Text(text = xmlStrings.greeting)

    }

    }

    View Slide

  130. Languages Support
    @Preview

    @Composable

    fun PostViewPreview() {



    val xmlStrings = rememberXmlStrings()

    ProvideXmlStrings(xmlStrings) {

    Text(text = xmlStrings.greeting)

    }

    }

    View Slide

  131. Languages Support
    @Preview

    @Composable

    fun PostViewPreview()

    9:41
    Post
    Hello World

    View Slide

  132. Languages Support
    @Preview(locale = “pt”)

    @Composable

    fun PostViewPreview()

    9:41
    Post
    Olá mundo

    View Slide

  133. Migration Phases
    0% Compose Milestones

    View Slide

  134. Tooling

    View Slide

  135. Kotlin Upgrades
    This version (1.1.1) of the Compose Compiler requires

    Kotlin version 1.6.10 but you appear to be using

    Kotlin version 1.6.20 which is not known to be compatible.

    View Slide

  136. Kotlin Upgrades
    composeOptions {

    kotlinCompilerExtensionVersion compose_version

    suppressKotlinVersionCompatibilityCheck true

    }

    Not Recommended

    View Slide

  137. Kotlin Upgrades
    ● Wait for compose to be updated

    View Slide

  138. View Slide

  139. Kotlin Upgrades
    ● Wait for compose to be updated

    ● Test out compose upgrades

    View Slide

  140. View Slide

  141. Kotlin Upgrades
    ● Wait for compose to be updated

    ● Test out compose upgrades

    View Slide

  142. Strategies for Migrating to Jetpack Compose
    ● Challenges

    ● How to think about migration

    ● Strategies & Tools

    View Slide

  143. Thank You!
    www.codingwithmohit.com
    @heyitsmohit

    View Slide