Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

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.

Fatih Giriş

October 27, 2021
Tweet

More Decks by Fatih Giriş

Other Decks in Programming

Transcript

  1. Integrating Compose • Entirely Compose • View based UI +

    Compose • Composables inside Views • Views inside composables
  2. 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
  3. Top-down class TopDownFragment : Fragment() { override fun onCreateView( inflater:

    LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return ComposeView(requireContext()) } } TopDownFragment.kt
  4. Top-down return ComposeView(requireContext()).apply { // Dispose the Composition when viewLifecycleOwner

    is destroyed setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner) ) setContent { // Compose World } } TopDownFragment.kt
  5. 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
  6. 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
  7. 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
  8. Top-down setContent { // Vertical Linear Layout Column { Button(onClick

    = {}) { Text("I am a compose button”) } } } TopDownFragment.kt
  9. Bottom-up class BottomUpFragment : Fragment() { override fun onViewCreated(view: View,

    savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) buttonComposeView.apply { // Configure ComposeView } } } BottomUpFragment.kt
  10. Bottom-up buttonComposeView.apply { // Dispose the Composition when viewLifecycleOwner is

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

    destroyed setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner) ) setContent { // In Compose world } } BottomUpFragment.kt
  12. 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
  13. Design System at DNB • Eufemia Design System 🌈 •

    Theming • Core components (EufemiaTextView, EufemiaButton etc.)
  14. 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
  15. Migration Plan • Pick a screen in the app •

    Apply bottom-up approach • Create components in Tetris & Eufemia • Iterate for all screens
  16. Expected View Layer • No Fragment • Activity sets content

    • Navigate between composables • Requires compose navigation library (if you don’t want to write your own)
  17. Migration Plan • Only change the way views are presented

    (ComposeView + Composables) • No change in navigation • After all screens are migrated, remove fragments
  18. Compose POC • Android Studio: Bumblebee Canary 1 • Android

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

    incompatibility ✅ Bypass version check or comment out the plugin :)
  20. 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
  21. Migration Status • Eufemia • Theming: Colors, typography, shape ✅

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

    • Make use of interoperability APIs • Stick to the migration plan