Mohit SarveiyaStrategies for Migrating to Jetpack Compose@heyitsmohit
View Slide
Strategies for Migrating to Jetpack Compose● Challenges● How to think about migration ● Strategies & Tools
Challenges
Code sizeCode sizeTime
Greenfield Project● Easier to use Compose● Fewer roadblocks to use Compose
Roadmap0% Compose100% Compose
Roadmap0% Compose100% ComposeRoadblockRoadblock
Roadmap● Long road● Obstacles along the way
Arch FragmentationCodebase
Arch FragmentationCodebaseLegacy code
Arch FragmentationCodebaseLegacy codeFeature AFeature B
Challenges● Interoperability
Interop● Compose to old view system
InteropCodebaseFeature ACompose ViewLegacy code
InteropCodebaseLegacy codeFeature ACompose View
Interop● Compose to old view system● Use old views in Compose
InteropCodebaseLegacy codeFeature ACustom View
InteropCodebaseFeature ACompose with Custom ViewLegacy code
Challenges● Interoperability● Architecture
ArchitectureCodebaseLegacy code
Arch Challenges● Not using composition over inheritance
Legacy CodeBase Fragment Too much logic
Legacy CodeBase Fragment Too much logicFragment Fragment Fragment…
Legacy CodeBase FragmentFragment Fragment Fragment…Refactor to Compose
Legacy CodeBase ViewHoldersToo much logic
Legacy CodeBase ViewHoldersToo much logicView Holder View Holder View Holder…Refactor to Compose
Arch Challenges● Not using composition over inheritance● Navigation
App ArchitectureAPIDomainNavigationUI
NavigationNavHost(navController, startDestination = "profile") {composable("profile") {Profile(/*...*/)}composable("friendslist") {FriendsList(/*...*/)}}
Legacy NavigationMain Activity Activity Activity
NavigationMain ActivityFragment A Fragment B Fragment C...
Challenges● Interoperability● Architecture● Testing
TestingCodebase 1% Code coverage5% Code coverage
Challenges● Interoperability● Architecture● Testing● Tooling
Compose UpgradesVersionTimealpha05alpha06
CollaborationOrgCI Arch Design Team Feature TeamsTeams
Strategies
Roadmap0% Compose100% ComposeMilestoneMilestone
Proposal● Specify milestones over time● Assess & Mitigate Risks ● Documentation
Roadmap0% Compose100% ComposeMilestone
ComplexityCodebaseHighLowMedium
Migration Phase 1ComplexityMigration timeLow
Migration Phase 2ComplexityMigration timeMedium
Migration Phase 1● Migrate Design Components
ExampleFeature A Feature B Feature CCommon Design Components
ExampleCommon Design ComponentsApp A App BApp C
Migration Phase 1● Migrate design components to Compose
Interoperability
Interop9:41
Interop9:41Convert to compose
Interop/>/>/>/>FrameLayout>9:41
Interop/>/>android:id="@+id/compose_view"android:layout_width="match_parent"android:layout_height="wrap_content"/>FrameLayout>
Compose Viewbinding.composeView.apply {setContent {}}
Compose Viewbinding.composeView.apply {setContent {MaterialTheme {DescriptionView()}}}
Challengesstrings.xmldimen.xmltheme.xmlComposeResuse
Compose View@Composablefun DescriptionView() {Text(text = stringResource(id = R.string.title))}
Compose View@Composablefun DescriptionView() {Text(modifier = Modifier.padding(dimensionResource(R.id.margin_small)))}
Challenges● How do you reuse dimen and string resources?● Interop Theming
Interop Themingimplementation “com.google.android.material:compose-theme-adapter:1.1.1”
Interop Themingbinding.composeView.apply {setContent {MaterialTheme {DescriptionView()}}}
Interop Themingbinding.composeView.apply {setContent {MdcTheme {DescriptionView()}}}
Challenges● How do you reuse dimen and string resources?● Interop Theming● View lifecycle
Compose Viewbinding.composeView.apply {setContent {setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)}}
Phase 1 MigrationCommon Design ComponentsApp A App BApp C
Common Design Components● View all compose components● Document design system
airbnb/ShowkaseCode ! Issues Pull RequestsShowkaseCompatible with Compose 1.0.4Showcase 1.0.0-beta12Showkase is an annotation-processor based Android library thathelps you organize, discover, search and visualize JetpackCompose UI elements.
Showkase9:41Appcom.appGeneralLogsOtherKeyline overlaySlow down animationsNetwork ActivityLogsLifecycle logsShow UX
Showkase9:41Appcom.appGeneralLogsOtherKeyline overlaySlow down animationsNetwork ActivityLogsLifecycle logsShow UX9:41Design ComponentsComponentsColorsTypography
ShowkaseCompose Design SpecGenerate
Showkase@ShowkaseRootclass AppRootModule: ShowkaseRootModule
ShowkasestartActivity(Showkase.getBrowserIntent(this))
Showkase@Preview(name = “Custom component", group = "Custom group")@Composablefun PostView()
Showkase9:41Design ComponentsPost viewTitle
Arch FragmentationCodebaseLegacy code100% Compose100% Compose
Interop@Composablefun PostView() {AndroidView(factory = { context->})
Interop@Composablefun PostView() {AndroidView(factory = { context->CustomView(context).apply {layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)}})
Interop● ComposeView● AndroidView
Migration Phase 2● Migrate medium complexity features● Setup scaffolding to catch regressions
Testing
Testing InfraCompose ConversionsTesting Infra
Goals● Catch regressions earlier● Increase covers with compose migration
Testing Infra● Snapshot testing● UI Testing Interop
cashapp/paparazziCode ! Issues Pull RequestsPaparazziAn Android library to render your application screens withouta physical device or emulator.
PaparazziScreenshotUnit Test
Paparazzi● 1.0.0-Snapshot has compose support
Paparazzi@get:Ruleval paparazzi = Paparazzi()
Paparazzi@get:Ruleval paparazzi = Paparazzi()@Testfun testView() {paparazzi.snapshot {PostView(uiState)} }
Paparazzi9:41PostTitle
PaparazziGit (LFS)Snapshot
Interop/>/>/>FrameLayout>9:41
Interop9:41Semantics Tree
Compose UI Testing@get:Ruleval composeTestRule = createAndroidComposeRule()
Compose UI Testing@Testfun postViewTest() {composeTestRule.onNodeWithText(“Title").assertIsDisplayed()}
Interop@Composablefun PostView() {AndroidView(factory = { context->CustomView(context)})}
Interop@Testfun postViewTest() {composeTestRule.onNodeWithText("Submit").performClick()}
Interop@Testfun postViewTest() {composeTestRule.onNodeWithText(“Submit").performClick()Espresso.onView(withText(“Success")).check(matches(isDisplayed()))}
Migration Phases0% Compose Milestones
Migration Phases● Milestones depend on complexity & code size● Low Complexity->High Complexity
Migration Phases0% Compose MilestonesTesting Infra
Internationalization
Languages Supportstrings.xmlvalues-envalues-devalues-frstrings.xmlstrings.xml
Languages SupportAdd strings
Languages SupportTranslation serviceAdd strings
Languages SupportTranslation serviceProvides translated stringsAdd strings
Languages Support● Compose language interop
adrielcafe/lyricistCode ! Issues Pull RequestsLyricistThe missing I18N and L10N multiplatform library for JetpackCompose!
Languages SupportStrings (xml)TypesKSPComposition Local
Languages Supportstrings.xmlvaluesvalues-ptstrings.xml
Languages SupportHello worldstring>Avocadoitem>string-array>%d zeroitem>plurals>resources>
Languages SupportOlá mundostring>Abacateitem>string-array>%d zeroitem>plurals>resources>
Languages Supportksp {arg("lyricist.xml.resourcesPath", android.sourceSets.main.res.srcDirs.first().absolutePath)arg("lyricist.xml.moduleName", “xml")arg("lyricist.xml.defaultLanguageTag", "en")}
Languages Supportdata class Strings(val greeting: String,val array: List,val plurals: (quantity: Int)->String)object Locales {val En = "en"val Pt = "pt"}
Languages Support@Composablepublic fun ProvideStrings() {CompositionLocalProvider(provider provides lyricist.strings)}
Languages Support@Preview@Composablefun PostViewPreview() {val xmlStrings = rememberXmlStrings()ProvideXmlStrings(xmlStrings) {Text(text = xmlStrings.greeting)}}
Languages Support@Preview@Composablefun PostViewPreview()9:41PostHello World
Languages Support@Preview(locale = “pt”)@Composablefun PostViewPreview()9:41PostOlá mundo
Tooling
Kotlin UpgradesThis 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.
Kotlin UpgradescomposeOptions { kotlinCompilerExtensionVersion compose_version suppressKotlinVersionCompatibilityCheck true}Not Recommended
Kotlin Upgrades● Wait for compose to be updated
Kotlin Upgrades● Wait for compose to be updated● Test out compose upgrades
Thank You!www.codingwithmohit.com@heyitsmohit