Slide 1

Slide 1 text

Incheon ࢶ঱ഋ UIীࢲ੄ ࢚కҙܻ - Circuitҗ Rinਸ ઺बਵ۽ ੉അ਋ Android/React Native Developer, Viva Republica(Toss) Organizer, Kotlin User Groups Seoul/GDG Incheon

Slide 2

Slide 2 text

࢚కҙܻ੄ ѐ֛җ ৉ࢎ ݾର ҙܻೡ “࢚క” ঌইࠁӝ ૑৉ ࢚క ઑӘ ؊ ੜ ҙܻ ೧ࠁӝ

Slide 3

Slide 3 text

Speakerdeck Youtube ࢶ঱ഋ UI ೨बѐ֛

Slide 4

Slide 4 text

࢚కҙܻ੄ ѐ֛җ ৉ࢎ

Slide 5

Slide 5 text

Before Compose class SampleActivity: AppCompatActivity() { private var name = "" override fun onCreate(bundle: SavedInstanceState?) { super.onCreate(bundle) // ࢲߡాन APIService.getMyName().enqueue(object: Callback { override fun onResponse(ޤदӽ੉) { name = response.body().name updateUi() // binding.tv.text = name } }) } }

Slide 6

Slide 6 text

Before Compose • ѐߊ੗о ૒੽ ߸ࣻܳ ߸҃ೞҊ UI੄ ࢚కܳ ߸҃ೞب۾ ௏٘ܳ ੘ ࢿ೧ঠೣ • ߸ࣻо ചݶ੄ ߸҃ਸ दఃחؘ ઺ਃبо ڄয૗ • ചݶਸ ߸҃ೞח Ѫ਷ ߸ࣻ ч੉ ইצ ѐߊ੗

Slide 7

Slide 7 text

Before Compose - DataBinding class SampleActivity: AppCompatActivity() { private val viewModel by viewModels() private lateinit var binding: ActivitySampleBinding override fun onCreate(bundle: SavedInstanceState?) { super.onCreate(bundle) binding = DataBindingUtil.setContentView(this, R.layout.ui) binding.viewModel = viewModel } } android:text=“@{vm.user.nam e}” />

Slide 8

Slide 8 text

Data Binding ߸ࣻܳ ߸҃݅ ೧ب ചݶ੄ ࢚కо ߸҃ؽ ߸ࣻо ചݶ੄ “࢚క”о غب۾ ࢸ҅ ߑೱ੉ ߸҃ ૊, ചݶ੄ ࢚క ߸҃ ઱୓о ѐߊ੗ীࢲ दझమਵ۽ ੹بؽ ঑ب੸ੋ ࠽٘ఋ੐ ࢚थҗ ೧ةೞӝ য۰਍ ী۞ झఖ ౟ۨ੉झ Data Binding -> View Bindingਵ۽ ֈযоח ୶ࣁ Ӓۢীب ࠛҳೞҊ ѐߊ੗о ചݶ੄ ߸ࣻо “࢚క”۽ ੋधೞѱ ػ ୐ ࣁ؀

Slide 9

Slide 9 text

Compose بੑ ੉റ @Composable fun SampleText() { var text by remember { mutableStateOf("") } LaunchedEffect(Unit) { text = APIService.getName().await() } TextField(value = text, onValueChange = { text = it }) }

Slide 10

Slide 10 text

Compose ߸ࣻܳ ߸҃݅ ೧ب ചݶ੄ ࢚కо ߸҃ؽ ୶о੸ੋ ࠽٘ కझ௼ হ੉ ߸ࣻо ചݶ੄ ࢚కо ؽ

Slide 11

Slide 11 text

Compose ߸ࣻо ചݶ੄ ࢚క੉׮ Android Developers - Ui State

Slide 12

Slide 12 text

࢚క ખ ؊ ੗ࣁ൤ ঌইࠁӝ

Slide 13

Slide 13 text

࢚కо ޤ৘ਃ? UIܳ ಴അೞח ݽٚ ؘ੉ఠ

Slide 14

Slide 14 text

࢚కҙܻо ޤ৘ਃ? UIܳ ಴അೞח ݽٚ ؘ੉ఠ ܳ ҙܻೞח ߑߨۿ

Slide 15

Slide 15 text

ࢤпࠁ׮ ҙܻ೧ঠೡ ࢚క੄ ઙܨח ׮নೞ׮ ഥਗоੑ ੑ۱ Form • ੉ܴ/੉ݫੌ ઱ࣗ/࠺޻ߣഐ ١ ੑ۱ UI • ੉ݫੌ઱ࣗ ઺ࠂ ৈࠗ • ࠺޻ߣഐ Ѩૐ • ࠺޻ߣഐ ഛੋҗ ࠺޻ߣഐ ੌ஖ ৈࠗ • ডҙ ز੄

Slide 16

Slide 16 text

ࢤпࠁ׮ ҙܻ೧ঠೡ ࢚క੄ ઙܨח ׮নೞ׮

Slide 17

Slide 17 text

࢚కҙܻ੄ Key Point ؘ੉ఠܳ য٣ী, যڌѱ ਤ஖दఃחо?

Slide 18

Slide 18 text

੹৉࢚క৬ ૑৉࢚క

Slide 19

Slide 19 text

੹৉࢚క৬ ૑৉࢚క ࢚కо ־ҳীѱ ҕਬغҊ ੓חо ೞա੄ ஹನք౟ && ੗ध ஹನք౟ٜীѱ ҕਬ - ૑৉࢚క ੹୓ ஹನք౟ٜ੉ ࢎਊೡ ࣻ ੓ѱ ҕਬ - ੹৉࢚క

Slide 20

Slide 20 text

੹৉ ࢚క(Global State) @Composable fun TodoTheme( darkTheme: Boolean = false, content: @Composable () -> Unit ) { val colors = todoColors() val typography = TodoTypography() ProvideTodoColorAndTypography(colors, typography){ MaterialTheme(content = content) } }

Slide 21

Slide 21 text

উ٘۽੉٘ীࢲ ੹৉࢚కҙܻ

Slide 22

Slide 22 text

উ٘۽੉٘ীࢲ ੹৉࢚కҙܻ

Slide 23

Slide 23 text

Configuration Change CompositionLocal۽ ੹׳غח ч੉ ੹৉ী ੹౵غפө Config Change۽ ч੉ ࠛউ੿೧૑ݶ ੹୓ ஹನք౟ী ৔ೱ -> জ উ੿ࢿ ੷ೞ ೐۽ࣁझ ࢤݺ઱ӝী উ੿੸ਵ۽ ؘ੉ఠ ੷੢द ਊ۝ ޙઁ rememberSavableਸ ഝਊೞݶ 1MB ੉ղ۽ ؘ੉ఠܳ ੷੢೧ঠೞחؘ ਗೞח ݅ఀ੄ ؘ੉ఠܳ ੷੢ਸ ೞ׮о ৘࢚஖ ޅೠ Exception੉ ఠ૕ ࣻ ੓਺ উ٘۽੉٘ীࢲ ੹৉࢚కҙܻ

Slide 24

Slide 24 text

੹৉࢚కҙܻ Android OS: ॄب જ਷ؘ, ੸׼൤ ॄۄ

Slide 25

Slide 25 text

૑৉࢚క ੜ ҙܻೞӝ

Slide 26

Slide 26 text

؀ࠗ࠙੄ ૑৉࢚కҙܻ ViewModel ❤

Slide 27

Slide 27 text

੿݈ ViewModelী ֍חѱ ୭ࢶ? Configuration Changeী ٮۄࢲ ч੉ ߸҃೧ঠೡ ࣻ ੓਺ ѓ۟द Fold৬ э਷ ҃਋ ੽ਵݶ Phone UI, ಟ஖ݶ Tablet UI ࠁৈ઻ঠೞח ா੉ झ ߊࢤ, ੉۠ ؘ੉ఠܳ Config Change উ੿੸੉ѱ فݶ য়൤۰ ী۞ ߊࢤ ׮ܲ ஹನք౟ٜী ࠛ೙ਃೠ ੿ࠁܳ ֈѹ п ё୓ח ޻੽ೞѱ ҙ҅ػ ׮ܲ ё୓ٜী ؀೧ࢲ ઁೠػ ૑ध݅ਸ оઉঠ ೠ׮. (Law of Demeter)

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

StateHolder Class @Stable class NiaAppState( val navController: NavHostController, coroutineScope: CoroutineScope, networkMonitor: NetworkMonitor, userNewsResourceRepository: UserNewsResourceRepository, timeZoneMonitor: TimeZoneMonitor, ) { private val previousDestination = mutableStateOf(null) ……

Slide 30

Slide 30 text

StateHolder Class @Composable fun rememberNiaAppState( … ): NiaAppState { return remember { NiaAppState( navController = navController, coroutineScope = coroutineScope, networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, timeZoneMonitor = timeZoneMonitor, )

Slide 31

Slide 31 text

State Holder https://developer.android.com/topic/architecture/ui-layer/ stateholders

Slide 32

Slide 32 text

Circuit ନݡೞӝ

Slide 33

Slide 33 text

Circuit? Circuit is a simple, lightweight, and extensible framework for building Kotlin applications that’s Compose from the ground up.

Slide 34

Slide 34 text

Circuit? Circuit਷ рױೞҊ о߶਋ݴ ഛ੢ оמೠ ೐ۨ੐ਕ௼۽, A-Zө૑ Compose݅ਸ ഝਊೞৈ Kotlin গ೒ܻா੉࣌ਸ ҳ୷ೡ ࣻ ੓णפ׮.

Slide 35

Slide 35 text

Circuit? ࢚కҙܻ ߑध݅ਸ ઱۽ ੉ঠӝ ೞӝ ٸޙী ജ҃ࢸ੿ ١ ҳഅী ೙ਃೠ ࣁࠗࢎ೦ٜ਷ ઁ৻ೞ৓णפ׮

Slide 36

Slide 36 text

Circuit?

Slide 37

Slide 37 text

Circuit?

Slide 38

Slide 38 text

Circuit੄ рױೠ ҳઑب

Slide 39

Slide 39 text

Screen data class CounterScreen(val initialCount: Int): Screen data class DetailScreen(val id: Long): Screen data class FavoriteScreen: Screen

Slide 40

Slide 40 text

CircuitUiState, CircuitUiEvent sealed inte rf ace State : CircuitUiState { data object Loading : State data object NoFavorites : State data class Results( val list: List, val eventSink: (Event) -> Unit ) : State } sealed inte rf ace Event : CircuitUiEvent { data class ClickFavorite(id: Long): Event }

Slide 41

Slide 41 text

@Composable fun CounterPresenter(): CounterState { var count by rememberSaveable { mutableStateOf(0) } return CounterState(count) { event -> when (event) { CounterEvent.Increment -> count++ CounterEvent.Decrement -> count-- } } } Presenter

Slide 42

Slide 42 text

@Composable fun CounterPresenter(): CounterState { var count by rememberRetained { mutableStateOf(0) } return CounterState(count) { event -> when (event) { CounterEvent.Increment -> count++ CounterEvent.Decrement -> count-- } } } Presenter

Slide 43

Slide 43 text

rememberRetained

Slide 44

Slide 44 text

// CircuitUiState੄ ఋੑ expo rt inte rf ace CounterState { value: number } // CircuitUiState ୡӝ ё୓ const initialState: CounterState = { value: 0, } Redux Toolkitҗ Presenter

Slide 45

Slide 45 text

// Circuit੄ presenter expo rt const counterSlice = createSlice({ name: 'counter', // Updateغח state initialState, reducers: { // event ੿੄ ࠗ࠙ increment: (state) => { state.value += 1 }, decrement: (state) => { state.value -= 1 } } ) Redux Toolkitҗ Presenter

Slide 46

Slide 46 text

@CircuitInject(FavoriteScreen::class, ActivityRetainedComponent::class) @Composable fun FavoritesList(state: FavoritesScreen.State) { when (state) { Loading -> Text(text = stringResource(R.string.loading_favorites)) NoFavorites -> Text( modi fi er = Modi fi er.testTag("no favorites"), text = stringResource(R.string.no_favorites) ) is Results -> { LazyColumn { items(state.list) { Favorite(it, state.eventSink) } } } } } Screen

Slide 47

Slide 47 text

Circuitী ؀ೠ рۚೠ ࣗх • ؀ഋ জҗ э਷ ҃਋, ఋੑ/ҳഅী ؀ೠ ӏઁٜ੉ ౱੉ ࢤ࢑ೞח ௏ ٘੄ ాੌࢿҗ оةࢿਸ ֫ৈ઴ ࣻ ੓਺ • Unit Testب рױೞѱ ੘ࢿೡ ࣻ ੓਺ • Compose runtimeਸ ഝਊ೧ࢲ ߈਽ഋ ࢚కܳ ઁ੘ೡ ࣻ ੓׮ח Ѫী ௾ ੄੄

Slide 48

Slide 48 text

Circuitী ؀ೠ рۚೠ ࣗх • ੉Ѧ ੸ਊೡ ࣻ ੓ח ഥࢎо ݻ੉ա ੓ਸө? • ׮নೠ بҳٜ(Flow, Rx, Legacy View ١)җ interopਸ ೡ ࣻ ੓૑݅, ੉ܳ interopೞӝ ਤ೧ ٘ח ݆ࣻ਷ ҕٜࣻਸ ࢤп೧ঠؽ • ъઁ۽ ҳഅ/ࢎਊ೧ঠೞח Ѫٜ(Presenter, Navigator ١)੉ ݆ই पઁ۽ ೐۽؋࣌ী ੸ਊؼ݅ೠ૑ח ੄ޙ

Slide 49

Slide 49 text

@Composable fun ScreenA() { var isB by rememberRetained { mutableStateOf(true) } rememberRetained{ "A" } if(isB) { rememberRetained{ "B" } } else { rememberRetained{ "C" } } } takahirom/Rin

Slide 50

Slide 50 text

উ٘۽੉٘ীࢲ੄ ࢚కҙܻ ੹৉࢚క৬ ૑৉࢚క ૑৉࢚కܳ ࠁ׮ ؊ ੜ ҙܻೠ ߑߨ Summary

Slide 51

Slide 51 text

Thank you Incheon HyunWoo Lee Android(React Native) Developer, Viva Republica(Toss)