Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

Jetpack Compose

Slide 3

Slide 3 text

How to migrate existing apps to Compose?

Slide 4

Slide 4 text

Agenda • Integrating Compose • Interoperability APIs • Migration strategies • Migration at DNB • Take-aways

Slide 5

Slide 5 text

Integrating Compose • Entirely Compose • View based UI + Compose • Composables inside Views • Views inside composables

Slide 6

Slide 6 text

Interoperability APIs • ComponentActivity.setContent • ComposeView (View) • AndroidView (Composable)

Slide 7

Slide 7 text

Migration Strategies • Top-down • Bottom-up

Slide 8

Slide 8 text

Top-down • Start with the top most view • Go deeper and deeper ⬇

Slide 9

Slide 9 text

Top-down fragment_top_down.xml

Slide 10

Slide 10 text

Top-down fragment_top_down.xml fancy_content.xml

Slide 11

Slide 11 text

Top-down fancy_content.xml fragment_top_down.xml

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

Top-down class TopDownFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return ComposeView(requireContext()) } } TopDownFragment.kt

Slide 14

Slide 14 text

Top-down return ComposeView(requireContext()) TopDownFragment.kt

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

Top-down setContent { // Vertical Linear Layout Column { Button(onClick = {}) { Text("I am a compose button”) } } } TopDownFragment.kt

Slide 21

Slide 21 text

Bottom-up • Start with the inner most UI elements • Go up and up ⬆

Slide 22

Slide 22 text

Bottom-up fragment_bottom_up.xml

Slide 23

Slide 23 text

Bottom-up fragment_bottom_up.xml

Slide 24

Slide 24 text

Bottom-up class BottomUpFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) buttonComposeView.apply { // Configure ComposeView } } } BottomUpFragment.kt

Slide 25

Slide 25 text

Bottom-up buttonComposeView.apply { // Configure ComposeView } BottomUpFragment.kt

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

Bottom-up fragment_bottom_up.xml

Slide 30

Slide 30 text

Bottom-up fragment_bottom_up.xml

Slide 31

Slide 31 text

Bottom-up fragment_bottom_up.xml

Slide 32

Slide 32 text

Bottom-up fragment_bottom_up.xml

Slide 33

Slide 33 text

Voilà 🎉

Slide 34

Slide 34 text

Design System at DNB • Eufemia Design System 🌈 • Theming • Core components (EufemiaTextView, EufemiaButton etc.)

Slide 35

Slide 35 text

Design System at DNB www.eufemia.dnb.no

Slide 36

Slide 36 text

Design System at DNB • Tetris 📱(WIP) • Common UI components (Toolbar etc.)

Slide 37

Slide 37 text

Design System at DNB Eufemia library

Slide 38

Slide 38 text

Design System at DNB Eufemia library Tetris library uses

Slide 39

Slide 39 text

Design System at DNB Tetris library uses

Slide 40

Slide 40 text

Design System at DNB Eufemia App1 App2 uses uses

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

How?

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Where to start?

Slide 45

Slide 45 text

Migration Plan • Pick a screen in the app • Apply bottom-up approach • Create components in Tetris & Eufemia • Iterate for all screens

Slide 46

Slide 46 text

During Migration Eufemia Compose Tetris Compose App1 App2 uses uses uses Eufemia uses

Slide 47

Slide 47 text

After Migration Eufemia Compose Tetris Compose App1 App2 uses uses uses

Slide 48

Slide 48 text

Current View Layer Activity + Fragment

Slide 49

Slide 49 text

Current View Layer Activity + Fragment • Single Activity • Jetpack Navigation

Slide 50

Slide 50 text

Expected View Layer • No Fragment • Activity sets content • Navigate between composables • Requires compose navigation library (if you don’t want to write your own)

Slide 51

Slide 51 text

How to replace Fragments with Composables?

Slide 52

Slide 52 text

We don’t for now

Slide 53

Slide 53 text

Migration Plan Activity + Fragment + Compose

Slide 54

Slide 54 text

Migration Plan • Only change the way views are presented (ComposeView + Composables) • No change in navigation • After all screens are migrated, remove fragments

Slide 55

Slide 55 text

Compose POC

Slide 56

Slide 56 text

Compose POC Toolbar Header Selectable item

Slide 57

Slide 57 text

Compose POC • Android Studio: Bumblebee Canary 1 • Android Gradle Plugin: 7.1.0-alpha01 • Kotlin: 1.5.21 • Compose: 1.0.1

Slide 58

Slide 58 text

Compose POC Gradle Android cache fix plugin + AGP 7.1.0-alpha01 incompatibility ✅ Bypass version check or comment out the plugin :)

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

Compose POC ✅ Update all Kotlin versions to the same version

Slide 62

Slide 62 text

Compose POC Toolbar ✅ Header ✅ Selectable item ✅

Slide 63

Slide 63 text

Compose POC ✅ Update androidx.activity version to 1.3.0

Slide 64

Slide 64 text

Compose POC

Slide 65

Slide 65 text

Migration Status • Eufemia • Theming: Colors, typography, shape ✅ • Core components 🚧 • Tetris 🚧 • Apps 🚧

Slide 66

Slide 66 text

Take-aways • Plan your migration • POC • Migrate slowly • Make use of interoperability APIs • Stick to the migration plan

Slide 67

Slide 67 text

QUESTIONS @fatih_grs

Slide 68

Slide 68 text

THANKS ♥