From d159ce53a0248ef7b944b58a875b62b0fef3d8c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Baiget?= Date: Tue, 4 Feb 2020 07:59:51 +0100 Subject: [PATCH] Add viewmodels --- Examples/DemoApp/androidApp/build.gradle.kts | 1 + .../androidApp/src/main/AndroidManifest.xml | 2 +- .../com/jtouzy/demo/app/di/appModule.kt | 6 +++ .../{MainActivity.kt => KomposeActivity.kt} | 13 ++++--- .../app/ui/characters/CharactersScreen.kt | 25 ++----------- .../app/ui/characters/CharactersViewModel.kt | 23 ++++++++++++ .../jtouzy/demo/app/ui/quote/QuoteScreen.kt | 27 +++----------- .../demo/app/ui/quote/QuoteViewModel.kt | 24 ++++++++++++ .../buildSrc/src/main/kotlin/Config.kt | 2 + .../Characters/CharactersAssembler.swift | 4 +- .../Modules/Characters/CharactersView.swift | 4 +- .../Modules/Quotes/QuotesAssembler.swift | 6 +-- .../DemoApp/Modules/Quotes/QuotesView.swift | 5 ++- .../ui/characters/CharactersInteractor.kt | 6 +++ .../demo/ui/characters/CharactersPresenter.kt | 5 --- ...sPresenterImpl.kt => CharactersUseCase.kt} | 22 ++++++----- .../jtouzy/demo/ui/quotes/QuotesInteractor.kt | 8 ++++ .../jtouzy/demo/ui/quotes/QuotesPresenter.kt | 5 --- .../demo/ui/quotes/QuotesPresenterImpl.kt | 34 ----------------- .../jtouzy/demo/ui/quotes/QuotesUseCase.kt | 37 +++++++++++++++++++ .../ui/characters/CharactersPresenterTest.kt | 29 +++++++++------ .../com/jtouzy/demo/ui/TestOverridesNative.kt | 2 +- 22 files changed, 166 insertions(+), 124 deletions(-) rename Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/{MainActivity.kt => KomposeActivity.kt} (62%) create mode 100644 Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/characters/CharactersViewModel.kt create mode 100644 Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/quote/QuoteViewModel.kt create mode 100644 Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/CharactersInteractor.kt delete mode 100644 Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/CharactersPresenter.kt rename Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/{CharactersPresenterImpl.kt => CharactersUseCase.kt} (52%) create mode 100644 Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesInteractor.kt delete mode 100644 Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesPresenter.kt delete mode 100644 Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesPresenterImpl.kt create mode 100644 Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesUseCase.kt diff --git a/Examples/DemoApp/androidApp/build.gradle.kts b/Examples/DemoApp/androidApp/build.gradle.kts index e207f87..28123bf 100644 --- a/Examples/DemoApp/androidApp/build.gradle.kts +++ b/Examples/DemoApp/androidApp/build.gradle.kts @@ -87,6 +87,7 @@ dependencies { implementation(Coroutines.android) implementation(AndroidX.appCompat) implementation(AndroidX.coreKtx) + implementation(AndroidX.viewModel) implementation(Libs.koinAndroid) implementation(Libs.timber) implementation(Libs.coil) diff --git a/Examples/DemoApp/androidApp/src/main/AndroidManifest.xml b/Examples/DemoApp/androidApp/src/main/AndroidManifest.xml index 1e3f527..fafbcad 100644 --- a/Examples/DemoApp/androidApp/src/main/AndroidManifest.xml +++ b/Examples/DemoApp/androidApp/src/main/AndroidManifest.xml @@ -19,7 +19,7 @@ tools:ignore="GoogleAppIndexingWarning" > - + diff --git a/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/di/appModule.kt b/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/di/appModule.kt index 13f91b3..9f83a52 100644 --- a/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/di/appModule.kt +++ b/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/di/appModule.kt @@ -1,9 +1,12 @@ package com.jtouzy.demo.app.di import com.jtouzy.demo.app.ui.NavigationManager +import com.jtouzy.demo.app.ui.characters.CharactersViewModel +import com.jtouzy.demo.app.ui.quote.QuoteViewModel import com.jtouzy.demo.cache.DataStore import com.jtouzy.demo.cache.InMemoryDataStore import com.jtouzy.demo.network.BreakingBadApi +import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.dsl.module val appModule = module { @@ -11,4 +14,7 @@ val appModule = module { single { NavigationManager() } single { BreakingBadApi() } single { InMemoryDataStore(get()) } + + viewModel { CharactersViewModel(get()) } + viewModel { QuoteViewModel(get()) } } diff --git a/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/MainActivity.kt b/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/KomposeActivity.kt similarity index 62% rename from Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/MainActivity.kt rename to Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/KomposeActivity.kt index 2bcdc4d..31e4b9c 100644 --- a/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/MainActivity.kt +++ b/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/KomposeActivity.kt @@ -6,15 +6,18 @@ import androidx.compose.Composable import androidx.ui.core.setContent import androidx.ui.material.MaterialTheme import com.jtouzy.demo.app.ui.characters.CharactersScreen +import com.jtouzy.demo.app.ui.characters.CharactersViewModel import com.jtouzy.demo.app.ui.common.themeColors import com.jtouzy.demo.app.ui.quote.QuoteScreen -import com.jtouzy.demo.cache.DataStore +import com.jtouzy.demo.app.ui.quote.QuoteViewModel import org.koin.android.ext.android.inject +import org.koin.androidx.viewmodel.ext.android.viewModel -class MainActivity : AppCompatActivity() { +class KomposeActivity : AppCompatActivity() { private val navigationManager by inject() - private val dataStore by inject() + private val charactersViewModel by viewModel() + private val quoteViewModel by viewModel() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -29,8 +32,8 @@ class MainActivity : AppCompatActivity() { private fun KomposeApp() { MaterialTheme(colors = themeColors) { when (val screen = navigationManager.currentScreen) { - Screen.Home -> CharactersScreen.show(navigationManager, dataStore) - is Screen.Quote -> QuoteScreen.show(navigationManager, dataStore, screen.character) + Screen.Home -> CharactersScreen(navigationManager, charactersViewModel).MainScreen() + is Screen.Quote -> QuoteScreen(navigationManager, quoteViewModel).MainScreen(screen.character) } } } diff --git a/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/characters/CharactersScreen.kt b/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/characters/CharactersScreen.kt index ce5c30d..83c6b16 100644 --- a/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/characters/CharactersScreen.kt +++ b/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/characters/CharactersScreen.kt @@ -19,34 +19,25 @@ import androidx.ui.material.ripple.Ripple import androidx.ui.res.stringResource import com.jtouzy.demo.app.R import com.jtouzy.demo.app.ui.NavigationManager -import com.jtouzy.demo.app.ui.ObservableStore import com.jtouzy.demo.app.ui.Screen import com.jtouzy.demo.app.ui.common.ErrorScreen import com.jtouzy.demo.app.ui.common.LoadingScreen import com.jtouzy.demo.app.ui.common.VectorImage import com.jtouzy.demo.app.ui.common.image -import com.jtouzy.demo.cache.DataStore -import com.jtouzy.demo.ui.Store -import com.jtouzy.demo.ui.characters.CharactersPresenter -import com.jtouzy.demo.ui.characters.CharactersPresenterImpl import com.jtouzy.demo.ui.characters.CharactersViewState import com.jtouzy.demo.ui.model.Character class CharactersScreen( private val navigationManager: NavigationManager, - private val store: Store, - presenter: CharactersPresenter + private val viewModel: CharactersViewModel ) { - init { - presenter.loadCharacters() - } - @Composable - private fun MainScreen() { + fun MainScreen() { + viewModel.loadCharacters() Column { TopAppBar(title = { Text(text = +stringResource(R.string.app_name)) }) - Crossfade(store.viewState) { state -> + Crossfade(viewModel.viewState) { state -> when (state) { CharactersViewState.Loading -> LoadingScreen() is CharactersViewState.Content -> CharacterList(state.characters) @@ -92,12 +83,4 @@ class CharactersScreen( } } } - - companion object { - fun show(navigationManager: NavigationManager, dataStore: DataStore) { - val store = ObservableStore(CharactersViewState.Loading) - val presenter = CharactersPresenterImpl(store, dataStore) - CharactersScreen(navigationManager, store, presenter).MainScreen() - } - } } diff --git a/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/characters/CharactersViewModel.kt b/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/characters/CharactersViewModel.kt new file mode 100644 index 0000000..7fcf7b0 --- /dev/null +++ b/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/characters/CharactersViewModel.kt @@ -0,0 +1,23 @@ +package com.jtouzy.demo.app.ui.characters + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.jtouzy.demo.app.ui.ObservableStore +import com.jtouzy.demo.cache.DataStore +import com.jtouzy.demo.ui.characters.CharactersUseCase +import com.jtouzy.demo.ui.characters.CharactersViewState +import kotlinx.coroutines.launch + +class CharactersViewModel(dataStore: DataStore) : ViewModel() { + + val viewState: CharactersViewState + get() = store.viewState + private val store = ObservableStore(CharactersViewState.Loading) + private val useCase = CharactersUseCase(store, dataStore) + + fun loadCharacters() { + viewModelScope.launch { + useCase.asyncLoadCharacters() + } + } +} diff --git a/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/quote/QuoteScreen.kt b/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/quote/QuoteScreen.kt index db7f19e..3a1147a 100644 --- a/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/quote/QuoteScreen.kt +++ b/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/quote/QuoteScreen.kt @@ -15,40 +15,31 @@ import androidx.ui.material.TopAppBar import androidx.ui.res.stringResource import com.jtouzy.demo.app.R import com.jtouzy.demo.app.ui.NavigationManager -import com.jtouzy.demo.app.ui.ObservableStore import com.jtouzy.demo.app.ui.common.ErrorScreen import com.jtouzy.demo.app.ui.common.LoadingScreen import com.jtouzy.demo.app.ui.common.VectorImageButton -import com.jtouzy.demo.cache.DataStore -import com.jtouzy.demo.ui.Store import com.jtouzy.demo.ui.model.Character import com.jtouzy.demo.ui.model.Quote -import com.jtouzy.demo.ui.quotes.QuotesPresenter -import com.jtouzy.demo.ui.quotes.QuotesPresenterImpl import com.jtouzy.demo.ui.quotes.QuotesViewState class QuoteScreen( private val navigationManager: NavigationManager, - private val store: Store, - presenter: QuotesPresenter + private val viewModel: QuoteViewModel ) { - init { - presenter.loadQuotes() - } - @Composable - private fun MainScreen() { + fun MainScreen(character: Character) { + viewModel.loadQuote(character) Column { TopAppBar( - title = { Text(store.viewState.title) }, + title = { Text(viewModel.viewState.title) }, navigationIcon = { VectorImageButton(R.drawable.ic_back) { navigationManager.pop() } } ) - Crossfade(store.viewState) { state -> + Crossfade(viewModel.viewState) { state -> when (state) { is QuotesViewState.Loading -> LoadingScreen() is QuotesViewState.Content -> QuoteList(state.quotes) @@ -87,12 +78,4 @@ class QuoteScreen( Divider(color = (+MaterialTheme.colors()).onBackground) } } - - companion object { - fun show(navigationManager: NavigationManager, dataStore: DataStore, character: Character) { - val store = ObservableStore(QuotesViewState.Loading(character.name)) - val presenter = QuotesPresenterImpl(store, dataStore, character) - QuoteScreen(navigationManager, store, presenter).MainScreen() - } - } } diff --git a/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/quote/QuoteViewModel.kt b/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/quote/QuoteViewModel.kt new file mode 100644 index 0000000..b2fd5cb --- /dev/null +++ b/Examples/DemoApp/androidApp/src/main/kotlin/com/jtouzy/demo/app/ui/quote/QuoteViewModel.kt @@ -0,0 +1,24 @@ +package com.jtouzy.demo.app.ui.quote + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.jtouzy.demo.app.ui.ObservableStore +import com.jtouzy.demo.cache.DataStore +import com.jtouzy.demo.ui.model.Character +import com.jtouzy.demo.ui.quotes.QuotesUseCase +import com.jtouzy.demo.ui.quotes.QuotesViewState +import kotlinx.coroutines.launch + +class QuoteViewModel(dataStore: DataStore) : ViewModel() { + + val viewState: QuotesViewState + get() = store.viewState + private val store = ObservableStore(QuotesViewState.Loading("")) + private val useCase = QuotesUseCase(store, dataStore) + + fun loadQuote(character: Character) { + viewModelScope.launch { + useCase.asyncLoadQuotes(character) + } + } +} diff --git a/Examples/DemoApp/buildSrc/src/main/kotlin/Config.kt b/Examples/DemoApp/buildSrc/src/main/kotlin/Config.kt index cebf9c9..740beb4 100644 --- a/Examples/DemoApp/buildSrc/src/main/kotlin/Config.kt +++ b/Examples/DemoApp/buildSrc/src/main/kotlin/Config.kt @@ -50,11 +50,13 @@ object AndroidX { private object Versions { const val appCompat = "1.1.0" const val coreKtx = "1.1.0" + const val viewModel = "2.1.0-beta01" const val compose = "0.1.0-dev03" } const val appCompat = "androidx.appcompat:appcompat:${Versions.appCompat}" const val coreKtx = "androidx.core:core-ktx:${Versions.coreKtx}" + const val viewModel = "androidx.lifecycle:lifecycle-viewmodel-ktx:${Versions.viewModel}" const val composeFoundation = "androidx.ui:ui-foundation:${Versions.compose}" const val composeFramework = "androidx.ui:ui-framework:${Versions.compose}" diff --git a/Examples/DemoApp/iosApp/DemoApp/Modules/Characters/CharactersAssembler.swift b/Examples/DemoApp/iosApp/DemoApp/Modules/Characters/CharactersAssembler.swift index 4282368..95290ae 100644 --- a/Examples/DemoApp/iosApp/DemoApp/Modules/Characters/CharactersAssembler.swift +++ b/Examples/DemoApp/iosApp/DemoApp/Modules/Characters/CharactersAssembler.swift @@ -11,7 +11,7 @@ import shared class CharactersAssembler { static func assemble() -> CharactersView { let store = ObservableStore(baseState: CharactersViewState.Loading()) - let presenter = CharactersPresenterImpl(store: store, dataStore: Factory.dataStore) - return CharactersView(presenter: presenter, store: store) + let useCase = CharactersUseCase(store: store, dataStore: Factory.dataStore) + return CharactersView(useCase: useCase, store: store) } } diff --git a/Examples/DemoApp/iosApp/DemoApp/Modules/Characters/CharactersView.swift b/Examples/DemoApp/iosApp/DemoApp/Modules/Characters/CharactersView.swift index 5c428ba..036dd26 100644 --- a/Examples/DemoApp/iosApp/DemoApp/Modules/Characters/CharactersView.swift +++ b/Examples/DemoApp/iosApp/DemoApp/Modules/Characters/CharactersView.swift @@ -13,7 +13,7 @@ import shared // MARK: View Layout // struct CharactersView: View { - let presenter: CharactersPresenter + let useCase: CharactersUseCase @ObservedObject var store: ObservableStore var viewState: CharactersViewState { store.state } @@ -55,6 +55,6 @@ struct CharactersView: View { // extension CharactersView { func onViewAppear() { - presenter.loadCharacters() + useCase.loadCharacters() } } diff --git a/Examples/DemoApp/iosApp/DemoApp/Modules/Quotes/QuotesAssembler.swift b/Examples/DemoApp/iosApp/DemoApp/Modules/Quotes/QuotesAssembler.swift index db2a860..25db420 100644 --- a/Examples/DemoApp/iosApp/DemoApp/Modules/Quotes/QuotesAssembler.swift +++ b/Examples/DemoApp/iosApp/DemoApp/Modules/Quotes/QuotesAssembler.swift @@ -13,9 +13,9 @@ class QuotesAssembler { let store = ObservableStore( baseState: QuotesViewState.Loading(title: character.name) ) - let presenter = QuotesPresenterImpl( - store: store, dataStore: Factory.dataStore, character: character + let useCase = QuotesUseCase( + store: store, dataStore: Factory.dataStore ) - return QuotesView(presenter: presenter, store: store) + return QuotesView(useCase: useCase, store: store, character: character) } } diff --git a/Examples/DemoApp/iosApp/DemoApp/Modules/Quotes/QuotesView.swift b/Examples/DemoApp/iosApp/DemoApp/Modules/Quotes/QuotesView.swift index a36ef4e..ee4ee86 100644 --- a/Examples/DemoApp/iosApp/DemoApp/Modules/Quotes/QuotesView.swift +++ b/Examples/DemoApp/iosApp/DemoApp/Modules/Quotes/QuotesView.swift @@ -13,8 +13,9 @@ import shared // MARK: View Layout // struct QuotesView: View { - let presenter: QuotesPresenter + let useCase: QuotesUseCase @ObservedObject var store: ObservableStore + let character: Character var viewState: QuotesViewState { store.state } var body: some View { @@ -57,6 +58,6 @@ struct QuotesView: View { // extension QuotesView { func onViewAppear() { - presenter.loadQuotes() + useCase.loadQuotes(character) } } diff --git a/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/CharactersInteractor.kt b/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/CharactersInteractor.kt new file mode 100644 index 0000000..d16f5b9 --- /dev/null +++ b/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/CharactersInteractor.kt @@ -0,0 +1,6 @@ +package com.jtouzy.demo.ui.characters + +interface CharactersInteractor { + suspend fun asyncLoadCharacters() + fun loadCharacters() +} diff --git a/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/CharactersPresenter.kt b/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/CharactersPresenter.kt deleted file mode 100644 index 940d81c..0000000 --- a/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/CharactersPresenter.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.jtouzy.demo.ui.characters - -interface CharactersPresenter { - fun loadCharacters() -} diff --git a/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/CharactersPresenterImpl.kt b/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/CharactersUseCase.kt similarity index 52% rename from Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/CharactersPresenterImpl.kt rename to Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/CharactersUseCase.kt index dfb2c8c..1bccc0d 100644 --- a/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/CharactersPresenterImpl.kt +++ b/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/characters/CharactersUseCase.kt @@ -9,20 +9,24 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -class CharactersPresenterImpl( +class CharactersUseCase( private val store: Store, private val dataStore: DataStore -) : CharactersPresenter { +) : CharactersInteractor { + + override suspend fun asyncLoadCharacters() { + store.viewState = CharactersViewState.Loading + try { + val characters = withContext(ioDispatcher) { dataStore.getCharacters() } + store.viewState = CharactersViewState.Content(characters.map { Character(it) }) + } catch (exception: Exception) { + store.viewState = CharactersViewState.Error + } + } override fun loadCharacters() { GlobalScope.launch(mainDispatcher) { - store.viewState = CharactersViewState.Loading - try { - val characters = withContext(ioDispatcher) { dataStore.getCharacters() } - store.viewState = CharactersViewState.Content(characters.map { Character(it) }) - } catch (exception: Exception) { - store.viewState = CharactersViewState.Error - } + loadCharacters() } } } diff --git a/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesInteractor.kt b/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesInteractor.kt new file mode 100644 index 0000000..9094999 --- /dev/null +++ b/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesInteractor.kt @@ -0,0 +1,8 @@ +package com.jtouzy.demo.ui.quotes + +import com.jtouzy.demo.ui.model.Character + +interface QuotesInteractor { + suspend fun asyncLoadQuotes(character: Character) + fun loadQuotes(character: Character) +} diff --git a/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesPresenter.kt b/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesPresenter.kt deleted file mode 100644 index 8012496..0000000 --- a/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesPresenter.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.jtouzy.demo.ui.quotes - -interface QuotesPresenter { - fun loadQuotes() -} diff --git a/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesPresenterImpl.kt b/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesPresenterImpl.kt deleted file mode 100644 index 15ef7ab..0000000 --- a/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesPresenterImpl.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.jtouzy.demo.ui.quotes - -import com.jtouzy.demo.cache.DataStore -import com.jtouzy.demo.ui.Store -import com.jtouzy.demo.ui.model.Character -import com.jtouzy.demo.ui.model.Quote -import com.jtouzy.demo.utils.ioDispatcher -import com.jtouzy.demo.utils.mainDispatcher -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -class QuotesPresenterImpl( - private val store: Store, - private val dataStore: DataStore, - private val character: Character -) : QuotesPresenter { - - override fun loadQuotes() { - GlobalScope.launch(mainDispatcher) { - store.viewState = QuotesViewState.Loading(character.name) - try { - val quotes = withContext(ioDispatcher) { dataStore.getCharacterQuotes(character.name) } - store.viewState = if (quotes.isEmpty()) { - QuotesViewState.NoQuote(character.name) - } else { - QuotesViewState.Content(character.name, quotes.map { Quote(it) }) - } - } catch (exception: Exception) { - store.viewState = QuotesViewState.Error(character.name) - } - } - } -} diff --git a/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesUseCase.kt b/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesUseCase.kt new file mode 100644 index 0000000..144176e --- /dev/null +++ b/Examples/DemoApp/shared/src/commonMain/kotlin/com/jtouzy/demo/ui/quotes/QuotesUseCase.kt @@ -0,0 +1,37 @@ +package com.jtouzy.demo.ui.quotes + +import com.jtouzy.demo.cache.DataStore +import com.jtouzy.demo.ui.Store +import com.jtouzy.demo.ui.model.Character +import com.jtouzy.demo.ui.model.Quote +import com.jtouzy.demo.utils.ioDispatcher +import com.jtouzy.demo.utils.mainDispatcher +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class QuotesUseCase( + private val store: Store, + private val dataStore: DataStore +) : QuotesInteractor { + + override suspend fun asyncLoadQuotes(character: Character) { + store.viewState = QuotesViewState.Loading(character.name) + try { + val quotes = withContext(ioDispatcher) { dataStore.getCharacterQuotes(character.name) } + store.viewState = if (quotes.isEmpty()) { + QuotesViewState.NoQuote(character.name) + } else { + QuotesViewState.Content(character.name, quotes.map { Quote(it) }) + } + } catch (exception: Exception) { + store.viewState = QuotesViewState.Error(character.name) + } + } + + override fun loadQuotes(character: Character) { + GlobalScope.launch(mainDispatcher) { + asyncLoadQuotes(character) + } + } +} diff --git a/Examples/DemoApp/shared/src/commonTest/kotlin/com/jtouzy/demo/ui/characters/CharactersPresenterTest.kt b/Examples/DemoApp/shared/src/commonTest/kotlin/com/jtouzy/demo/ui/characters/CharactersPresenterTest.kt index 3a31419..7ea66df 100644 --- a/Examples/DemoApp/shared/src/commonTest/kotlin/com/jtouzy/demo/ui/characters/CharactersPresenterTest.kt +++ b/Examples/DemoApp/shared/src/commonTest/kotlin/com/jtouzy/demo/ui/characters/CharactersPresenterTest.kt @@ -1,6 +1,7 @@ package com.jtouzy.demo.ui.characters import com.jtouzy.demo.cache.DataStore +import com.jtouzy.demo.common.runTest import com.jtouzy.demo.network.dto.CharacterDto import com.jtouzy.demo.ui.Store import com.jtouzy.demo.ui.model.Character @@ -10,7 +11,7 @@ import io.mockk.mockk import io.mockk.spyk import kotlin.test.Test -abstract class CharactersPresenterTest { +open class CharactersPresenterTest { private val dtoCharacters = listOf( CharacterDto("Toto", "url"), @@ -27,26 +28,30 @@ abstract class CharactersPresenterTest { private val dataStore = mockk { coEvery { getCharacters() } returns dtoCharacters } - private val presenter = CharactersPresenterImpl(store, dataStore) + private val presenter = CharactersUseCase(store, dataStore) @Test fun `should return characters when call is successful`() { - presenter.loadCharacters() + runTest { + presenter.loadCharacters() - coVerifyOrder { - store.viewState = CharactersViewState.Loading - store.viewState = CharactersViewState.Content(characters) + coVerifyOrder { + store.viewState = CharactersViewState.Loading + store.viewState = CharactersViewState.Content(characters) + } } } @Test fun `should return an error when call is unsuccessful`() { - coEvery { dataStore.getCharacters() } throws IllegalStateException("error") - presenter.loadCharacters() - - coVerifyOrder { - store.viewState = CharactersViewState.Loading - store.viewState = CharactersViewState.Error + runTest { + coEvery { dataStore.getCharacters() } throws IllegalStateException("error") + presenter.loadCharacters() + + coVerifyOrder { + store.viewState = CharactersViewState.Loading + store.viewState = CharactersViewState.Error + } } } } diff --git a/Examples/DemoApp/shared/src/iosTest/kotlin/com/jtouzy/demo/ui/TestOverridesNative.kt b/Examples/DemoApp/shared/src/iosTest/kotlin/com/jtouzy/demo/ui/TestOverridesNative.kt index 580e972..6e16395 100644 --- a/Examples/DemoApp/shared/src/iosTest/kotlin/com/jtouzy/demo/ui/TestOverridesNative.kt +++ b/Examples/DemoApp/shared/src/iosTest/kotlin/com/jtouzy/demo/ui/TestOverridesNative.kt @@ -2,4 +2,4 @@ package com.jtouzy.demo.ui import com.jtouzy.demo.ui.characters.CharactersPresenterTest -class CharactersPresenterTestJVM : CharactersPresenterTest() +class CharactersPresenterTestNative : CharactersPresenterTest()