Save 37% off PRO during our Black Friday Sale! »

Migrating a large-scale banking app to compose

Migrating a large-scale banking app to compose

The Android community adopts Jetpack Compose more and more each day. But what about our existing apps? Adopting existing apps to Jetpack Compose might be an overwhelming task when the migration is not planned well.

In this talk, we will see the Compose migration strategy used in DNB. We will briefly talk about the interoperability APIs and how they are used in this migration process. We will also talk about how we are adopting our internal design system to Jetpack Compose. Finally, we will discuss the challenges we faced during this migration.

7a454a0e47136cf3f9a1048537e283db?s=128

Fatih Giriş

October 27, 2021
Tweet

Transcript

  1. Migrating a large-scale banking app to Compose S. Fatih Giris

    @fatih_grs
  2. Jetpack Compose

  3. How to migrate existing apps to Compose?

  4. Agenda • Integrating Compose • Interoperability APIs • Migration strategies

    • Migration at DNB • Take-aways
  5. Integrating Compose • Entirely Compose • View based UI +

    Compose • Composables inside Views • Views inside composables
  6. Interoperability APIs • ComponentActivity.setContent • ComposeView (View) • AndroidView (Composable)

  7. Migration Strategies • Top-down • Bottom-up

  8. Top-down • Start with the top most view • Go

    deeper and deeper ⬇
  9. Top-down <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>

    fragment_top_down.xml
  10. Top-down <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> </LinearLayout> fragment_top_down.xml <Button android:layout_width="wrap_content" android:layout_height="wrap_content"

    /> fancy_content.xml
  11. Top-down <androidx.compose.ui.platform.ComposeView android:id="@+id/composeView" android:layout_width="match_parent" android:layout_height="match_parent" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" />

    fancy_content.xml fragment_top_down.xml
  12. Top-down class TopDownFragment : Fragment() { override fun onCreateView( inflater:

    LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate( R.layout.fragment_top_down, container, false ) } } TopDownFragment.kt
  13. Top-down class TopDownFragment : Fragment() { override fun onCreateView( inflater:

    LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return ComposeView(requireContext()) } } TopDownFragment.kt
  14. Top-down return ComposeView(requireContext()) TopDownFragment.kt

  15. Top-down return ComposeView(requireContext()).apply { // Dispose the Composition when viewLifecycleOwner

    is destroyed setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner) ) setContent { // Compose World } } TopDownFragment.kt
  16. Top-down return ComposeView(requireContext()).apply { // Dispose the Composition when viewLifecycleOwner

    is destroyed setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner) ) setContent { // Compose World // Vertical Linear Layout Column { // Add views as AndroidView by creating // programatically or inflating from XML } } } TopDownFragment.kt
  17. Top-down setContent { // Vertical Linear Layout Column { }

    } TopDownFragment.kt
  18. Top-down setContent { // Vertical Linear Layout Column { AndroidView(

    modifier = Modifier.fillMaxSize(), factory = { context -> // Inflate it from XML inflater.inflate( R.layout.fancy_content, container, false ) }, update = { view -> // View's been inflated or state read in // this block has been updated } ) } } TopDownFragment.kt
  19. Top-down setContent { // Vertical Linear Layout Column { AndroidView(

    modifier = Modifier.fillMaxSize(), factory = { context -> // Create view programatically // android.widget.Button Button(requireContext()) }, update = { view -> // View's been inflated or state read in // this block has been updated } ) } } TopDownFragment.kt
  20. Top-down setContent { // Vertical Linear Layout Column { Button(onClick

    = {}) { Text("I am a compose button”) } } } TopDownFragment.kt
  21. Bottom-up • Start with the inner most UI elements •

    Go up and up ⬆
  22. Bottom-up fragment_bottom_up.xml <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView

    android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
  23. Bottom-up fragment_bottom_up.xml <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.compose.ui.platform.ComposeView android:id="@+id/buttonComposeView" android:layout_width="wrap_content" android:layout_height="wrap_content" />

    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
  24. Bottom-up class BottomUpFragment : Fragment() { override fun onViewCreated(view: View,

    savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) buttonComposeView.apply { // Configure ComposeView } } } BottomUpFragment.kt
  25. Bottom-up buttonComposeView.apply { // Configure ComposeView } BottomUpFragment.kt

  26. Bottom-up buttonComposeView.apply { // Dispose the Composition when viewLifecycleOwner is

    destroyed setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner) ) } BottomUpFragment.kt
  27. Bottom-up buttonComposeView.apply { // Dispose the Composition when viewLifecycleOwner is

    destroyed setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner) ) setContent { // In Compose world } } BottomUpFragment.kt
  28. Bottom-up buttonComposeView.apply { // Dispose the Composition when viewLifecycleOwner is

    destroyed setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner) ) setContent { // In Compose world Button(onClick = {}) { Text(text = "I am migrated Button") } } } BottomUpFragment.kt
  29. Bottom-up fragment_bottom_up.xml <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.compose.ui.platform.ComposeView android:id="@+id/buttonComposeView" android:layout_width="wrap_content" android:layout_height="wrap_content" />

    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
  30. Bottom-up fragment_bottom_up.xml <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.compose.ui.platform.ComposeView android:id="@+id/buttonComposeView" android:layout_width="wrap_content" android:layout_height="wrap_content" />

    <androidx.compose.ui.platform.ComposeView android:id="@+id/textComposeView" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
  31. Bottom-up fragment_bottom_up.xml <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" > </LinearLayout>

  32. Bottom-up fragment_bottom_up.xml <androidx.compose.ui.platform.ComposeView android:id="@+id/fragmentComposeView" android:layout_width="match_parent" android:layout_height="match_parent" />

  33. Voilà 🎉

  34. Design System at DNB • Eufemia Design System 🌈 •

    Theming • Core components (EufemiaTextView, EufemiaButton etc.)
  35. Design System at DNB www.eufemia.dnb.no

  36. Design System at DNB • Tetris 📱(WIP) • Common UI

    components (Toolbar etc.)
  37. Design System at DNB Eufemia library

  38. Design System at DNB Eufemia library Tetris library uses

  39. Design System at DNB Tetris library uses

  40. Design System at DNB Eufemia App1 App2 uses uses

  41. Design System at DNB Eufemia Tetris (WIP) App1 App2 uses

    uses uses
  42. How?

  43. Migration Plan • Start with Eufemia & Tetris • Eufemia

    • Migrate design system to Compose (colours, shapes, typography) • Convert each component to Compose one by one • Tetris • Create Components as composables from the scratch • Use AndroidView for Eufemia Android Views
  44. Where to start?

  45. Migration Plan • Pick a screen in the app •

    Apply bottom-up approach • Create components in Tetris & Eufemia • Iterate for all screens
  46. During Migration Eufemia Compose Tetris Compose App1 App2 uses uses

    uses Eufemia uses
  47. After Migration Eufemia Compose Tetris Compose App1 App2 uses uses

    uses
  48. Current View Layer Activity + Fragment

  49. Current View Layer Activity + Fragment • Single Activity •

    Jetpack Navigation
  50. Expected View Layer • No Fragment • Activity sets content

    • Navigate between composables • Requires compose navigation library (if you don’t want to write your own)
  51. How to replace Fragments with Composables?

  52. We don’t for now

  53. Migration Plan Activity + Fragment + Compose

  54. Migration Plan • Only change the way views are presented

    (ComposeView + Composables) • No change in navigation • After all screens are migrated, remove fragments
  55. Compose POC

  56. Compose POC Toolbar Header Selectable item

  57. Compose POC • Android Studio: Bumblebee Canary 1 • Android

    Gradle Plugin: 7.1.0-alpha01 • Kotlin: 1.5.21 • Compose: 1.0.1
  58. Compose POC Gradle Android cache fix plugin + AGP 7.1.0-alpha01

    incompatibility ✅ Bypass version check or comment out the plugin :)
  59. Compose POC Hilt 2.38.1 + AGP 7.1.0-alpha01 bug* *https://github.com/google/dagger/issues/2618 ✅

    Update AGP to 7.1.0-alpha06
  60. Compose POC 2 files found with path 'META-INF/ui_release.kotlin_module' from inputs:

    - /Users/fatih/.gradle/caches/transforms-3/90edb5b9f55f1931b87d2b5c8a7aa61d/… - /Users/fatih/.gradle/caches/transforms-3/52dda4f473a3d3633ea54e66766f28d2/… ✅ Exclude META-INF/ui_release.kotlin_module in packaging options
  61. Compose POC ✅ Update all Kotlin versions to the same

    version
  62. Compose POC Toolbar ✅ Header ✅ Selectable item ✅

  63. Compose POC ✅ Update androidx.activity version to 1.3.0

  64. Compose POC

  65. Migration Status • Eufemia • Theming: Colors, typography, shape ✅

    • Core components 🚧 • Tetris 🚧 • Apps 🚧
  66. Take-aways • Plan your migration • POC • Migrate slowly

    • Make use of interoperability APIs • Stick to the migration plan
  67. QUESTIONS @fatih_grs

  68. THANKS ♥