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

  2. Strategies for Migrating to Jetpack Compose • Challenges • How

    to think about migration • Strategies & Tools
  3. Challenges

  4. Code size Code 
 size Time

  5. Greenfield Project • Easier to use Compose • Fewer roadblocks

    to use Compose
  6. Code size Code 
 size Time

  7. Roadmap 0% Compose 100% Compose

  8. Roadmap 0% Compose 100% Compose Roadblock Roadblock

  9. Roadmap • Long road • Obstacles along the way

  10. Arch Fragmentation Codebase

  11. Arch Fragmentation Codebase Legacy code

  12. Arch Fragmentation Codebase Legacy code Feature A Feature B

  13. Challenges • Interoperability

  14. Interop • Compose to old view system

  15. Interop Codebase Feature A Compose View Legacy code

  16. Interop Codebase Legacy code Feature A Compose View

  17. Interop • Compose to old view system • Use old

    views in Compose
  18. Interop Codebase Legacy code Feature A Custom View

  19. Interop Codebase Feature A Compose with 
 Custom View Legacy

    code
  20. Challenges • Interoperability • Architecture

  21. Architecture Codebase Legacy code

  22. Arch Challenges • Not using composition over inheritance

  23. Legacy Code Base Fragment Too much logic

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

  25. Legacy Code Base Fragment Fragment Fragment Fragment … Refactor to

    Compose
  26. Legacy Code Base View Holders Too much logic

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

    View Holder View Holder … Refactor to Compose
  28. Arch Challenges • Not using composition over inheritance • Navigation

  29. App Architecture API Domain Navigation UI

  30. Navigation NavHost(navController, startDestination = "profile") { composable("profile") { Profile( /*...*/

    ) } composable("friendslist") { FriendsList( /*...*/ ) } }
  31. Legacy Navigation Main Activity Activity Activity

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

  33. Challenges • Interoperability • Architecture • Testing

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

  35. Challenges • Interoperability • Architecture • Testing • Tooling

  36. Compose Upgrades Version Time alpha05 alpha06

  37. Challenges • Interoperability • Architecture • Testing • Tooling

  38. Collaboration Org CI Arch Design Team Feature Teams Teams

  39. Strategies

  40. Roadmap 0% Compose 100% Compose

  41. Roadmap 0% Compose 100% Compose Milestone Milestone

  42. Proposal • Specify milestones over time • Assess & Mitigate

    Risks • Documentation
  43. Roadmap 0% Compose 100% Compose Milestone

  44. Complexity Codebase High Low Medium

  45. Migration Phase 1 Complexity Migration time Low

  46. Migration Phase 2 Complexity Migration time Medium

  47. Migration Phase 1 • Migrate Design Components

  48. Example Feature A Feature B Feature C Common 
 Design

    Components
  49. Example Common 
 Design Components App A App B App

    C
  50. Collaboration Org CI Arch Design Team Feature Teams Teams

  51. Migration Phase 1 • Migrate design components to Compose

  52. Interoperability

  53. Interop 9:41

  54. Interop 9:41 Convert to compose

  55. Interop <ConstraintLayout> <ImageView /> <Button /> <TextView /> <TextView />

    </ FrameLayout> 9:41
  56. Interop <ConstraintLayout> <ImageView /> <Button /> <TextView /> <TextView />

    </ FrameLayout> 9:41
  57. Interop <ConstraintLayout> <ImageView /> <Button /> <androidx.compose.ui.platform.ComposeView android:id="@+id/compose_view" android:layout_width="match_parent" android:layout_height="wrap_content"

    /> </ FrameLayout>
  58. Compose View binding.composeView.apply { setContent { } }

  59. Compose View binding.composeView.apply { setContent { MaterialTheme { DescriptionView() }

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

  61. Compose View @Composable fun DescriptionView() { Text( text = stringResource(id

    = R.string.title) ) }
  62. Compose View @Composable fun DescriptionView() { Text( modifier = Modifier.padding(dimensionResource(R.id.margin_small))

    ) }
  63. Challenges • How do you reuse dimen and string resources?

    • Interop Theming
  64. Challenges strings.xml dimen.xml theme.xml Compose Resuse

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

  66. Interop Theming binding.composeView.apply { setContent { MaterialTheme { DescriptionView() }

    } }
  67. Interop Theming binding.composeView.apply { setContent { MdcTheme { DescriptionView() }

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

    • Interop Theming • View lifecycle
  69. Compose View binding.composeView.apply { setContent { setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed ) }

    }
  70. Phase 1 Migration Common 
 Design Components App A App

    B App C
  71. Common Design Components • View all compose components • Document

    design system
  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.
  73. Showkase 9:41 App com.app General Logs Other Keyline overlay Slow

    down animations Network Activity Logs Lifecycle logs Show UX
  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
  75. Showkase Compose Design Spec Generate

  76. Showkase @ShowkaseRoot class AppRootModule: ShowkaseRootModule

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

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

    fun PostView()
  79. Showkase 9:41 Design Components Post view Title

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

  81. Interop @Composable fun PostView() { AndroidView(factory = { context ->

    })
  82. Interop @Composable fun PostView() { AndroidView(factory = { context ->

    CustomView(context).apply { layoutParams = LinearLayout.LayoutParams( MATCH_PARENT, WRAP_CONTENT ) } })
  83. Interop • ComposeView • AndroidView

  84. Migration Phase 1 • Migrate Design Components

  85. Roadmap 0% Compose 100% Compose Milestone

  86. Roadmap 0% Compose 100% Compose Milestone Milestone

  87. Migration Phase 2 Complexity Migration time Medium

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

  89. Migration Phase 2 • Migrate medium complexity features • Setup

    scaffolding to catch regressions
  90. Testing

  91. Testing Infra Compose Conversions Testing Infra

  92. Goals • Catch regressions earlier • Increase covers with compose

    migration
  93. Testing Infra • Snapshot testing • UI Testing Interop

  94. cashapp/paparazzi Code ! Issues Pull Requests Paparazzi An Android library

    to render your application screens without a physical device or emulator.
  95. Paparazzi Screenshot Unit Test

  96. Paparazzi • 1.0.0-Snapshot has compose support

  97. Paparazzi @get:Rule val paparazzi = Paparazzi()

  98. Paparazzi @get:Rule val paparazzi = Paparazzi() @Test fun testView() {

    paparazzi.snapshot { PostView(uiState) } }
  99. Paparazzi 9:41 Post Title

  100. Paparazzi Git (LFS) Snapshot

  101. Testing Infra • Snapshot testing • UI Testing Interop

  102. Interop <ConstraintLayout> <ImageView /> <Button /> <ComposeView /> </ FrameLayout>

    9:41
  103. Interop 9:41 Semantics Tree

  104. Compose UI Testing @get:Rule val composeTestRule = createAndroidComposeRule<MainActivity>()

  105. Compose UI Testing @Test fun postViewTest() { composeTestRule.onNodeWithText(“Title").assertIsDisplayed() }

  106. Interop @Composable fun PostView() { AndroidView(factory = { context ->

    CustomView(context) }) }
  107. Interop @Test fun postViewTest() { composeTestRule.onNodeWithText("Submit").performClick() }

  108. Interop @Test fun postViewTest() { composeTestRule.onNodeWithText(“Submit").performClick() Espresso.onView(withText(“Success")).check(matches(isDisplayed())) }

  109. Migration Phase 2 • Migrate medium complexity features • Setup

    scaffolding to catch regressions
  110. Migration Phases 0% Compose Milestones

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

    • Low Complexity -> High Complexity
  112. Migration Phases 0% Compose Milestones Testing Infra

  113. Internationalization

  114. Migration Phases 0% Compose Milestones

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

  116. Languages Support Add strings

  117. Languages Support Translation service Add strings

  118. Languages Support Translation service Provides translated strings Add strings

  119. Languages Support • Compose language interop

  120. adrielcafe/lyricist Code ! Issues Pull Requests Lyricist The missing I18N

    and L10N multiplatform library for Jetpack Compose!
  121. Languages Support Strings 
 (xml) Types KSP Composition 
 Local

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

  123. Languages Support <resources> <string name=“greeting”>Hello world </ string> <string-array name="array">

    <item>Avocado </ item> </ string-array> <plurals name="plurals"> <item quantity="zero">%d zero </ item> </ plurals> </ resources>
  124. Languages Support <resources> <string name=“greeting”>Olá mundo </ string> <string-array name="array">

    <item>Abacate </ item> </ string-array> <plurals name="plurals"> <item quantity="zero">%d zero </ item> </ plurals> </ resources>
  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") }
  126. Languages Support data class Strings( val greeting: String, val array:

    List<String>, val plurals: (quantity: Int) -> String ) object Locales { val En = "en" val Pt = "pt" }
  127. Languages Support @Composable public fun <T> ProvideStrings() { CompositionLocalProvider( provider

    provides lyricist.strings ) }
  128. Languages Support @Preview @Composable fun PostViewPreview() { val xmlStrings =

    rememberXmlStrings() ProvideXmlStrings(xmlStrings) { Text(text = xmlStrings.greeting) } }
  129. Languages Support @Preview @Composable fun PostViewPreview() { val xmlStrings =

    rememberXmlStrings() ProvideXmlStrings(xmlStrings) { Text(text = xmlStrings.greeting) } }
  130. Languages Support @Preview @Composable fun PostViewPreview() { val xmlStrings =

    rememberXmlStrings() ProvideXmlStrings(xmlStrings) { Text(text = xmlStrings.greeting) } }
  131. Languages Support @Preview @Composable fun PostViewPreview() 9:41 Post Hello World

  132. Languages Support @Preview(locale = “pt”) @Composable fun PostViewPreview() 9:41 Post

    Olá mundo
  133. Migration Phases 0% Compose Milestones

  134. Tooling

  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.
  136. Kotlin Upgrades composeOptions { 
 kotlinCompilerExtensionVersion compose_version 
 suppressKotlinVersionCompatibilityCheck true

    } Not Recommended
  137. Kotlin Upgrades • Wait for compose to be updated

  138. None
  139. Kotlin Upgrades • Wait for compose to be updated •

    Test out compose upgrades
  140. None
  141. Kotlin Upgrades • Wait for compose to be updated •

    Test out compose upgrades
  142. Strategies for Migrating to Jetpack Compose • Challenges • How

    to think about migration • Strategies & Tools
  143. Thank You! www.codingwithmohit.com @heyitsmohit