Skip to content

Commit

Permalink
demo api, add result
Browse files Browse the repository at this point in the history
  • Loading branch information
hoangchungk53qx1 committed Aug 10, 2024
1 parent 3ad5103 commit e9fda80
Show file tree
Hide file tree
Showing 22 changed files with 285 additions and 52 deletions.
6 changes: 4 additions & 2 deletions common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,11 @@ kotlin {
implementation(libs.image.loader)

implementation(libs.immutable.collection)
implementation(libs.arrow.kt)

}
implementation(libs.kotlin.result)
implementation(libs.kotlin.result.coroutines)

}

sourceSets.commonMain {
kotlin.srcDir("build/generated/ksp/metadata")
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.vn.chungha.pet_kmm.data.mapper

import com.vn.chungha.pet_kmm.data.remote.response.PetCatResponse
import com.vn.chungha.pet_kmm.domain.model.PetModel

fun PetCatResponse.toPetCat() = PetModel(
id = id,
name = name,
description = description,
url = image.url
)
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ class PetApi(
private val client: HttpClient,
) : KoinComponent {

suspend fun fetchPetHomeByBreedPage(page: Int, limit : Int) = client
suspend fun fetchPetHomeByBreedPage(page: Int, limit : Int): List<PetCatResponse> = client
.get("$baseUrl/breeds?attach_breed=0&page=$page&limit=$limit").body<List<PetCatResponse>>()
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
package com.vn.chungha.pet_kmm.data.repository

import androidx.compose.animation.core.rememberTransition
import com.github.michaelbull.result.coroutines.runSuspendCatching
import com.github.michaelbull.result.Result
import com.vn.chungha.pet_kmm.data.remote.PetApi
import com.vn.chungha.pet_kmm.data.remote.response.PetCatResponse
import com.vn.chungha.pet_kmm.domain.PetCatRepository
import com.vn.chungha.pet_kmm.domain.mapper.toPetModel
import com.vn.chungha.pet_kmm.domain.model.PetModel
import com.vn.chungha.pet_kmm.utils.AppCoroutineDispatchers
import kotlinx.coroutines.withContext

class PetHomeRepositoryIml(private val petApi: PetApi) : PetCatRepository {
override suspend fun getPetCatByPage(
query: String,
page: Int,
perPage: Int
): List<PetCatResponse> =
petApi.fetchPetHomeByBreedPage(0, 20)
class PetHomeRepositoryIml(
private val petApi: PetApi,
private val appCoroutineDispatchers: AppCoroutineDispatchers,
) :
PetCatRepository {
override suspend fun getPetCatByPage(
query: String,
page: Int,
perPage: Int,
): Result<List<PetModel>, Throwable> {
return withContext(appCoroutineDispatchers.io) {
runSuspendCatching {
petApi.fetchPetHomeByBreedPage(page, perPage).map {
it.toPetModel()
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import com.vn.chungha.pet_kmm.data.remote.PetApi
import com.vn.chungha.pet_kmm.domain.PetCatRepository
import com.vn.chungha.pet_kmm.platformModule
import com.vn.chungha.pet_kmm.presentation.home.HomePetViewModel
import com.vn.chungha.pet_kmm.utils.AppCoroutineDispatcherImpl
import com.vn.chungha.pet_kmm.utils.AppCoroutineDispatchers
import io.ktor.client.HttpClient
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.plugins.DefaultRequest
Expand Down Expand Up @@ -37,10 +39,11 @@ private fun getBaseUrlDomain() = BuildKonfig.API_DOMAIN
fun commonModule(enableNetworkLogs: Boolean) = module {
factory(qualifier = named("baseUrl")) { getBaseUrlDomain() }
single { createJson() }
single<AppCoroutineDispatchers> { AppCoroutineDispatcherImpl() }
single { createHttpClient(get(), get(), enableNetworkLogs = enableNetworkLogs) }
single { PetApi(get(qualifier = named("baseUrl")), get()) }
single<PetCatRepository> { PetHomeRepositoryIml(get()) }
// viewModel { HomePetViewModel(get()) }
single<PetCatRepository> { PetHomeRepositoryIml(get(), get()) }
viewModel { HomePetViewModel() }
}

fun createJson() = Json {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@file:Suppress("PackageDirectoryMismatch")

package com.vn.chungha.pet_kmm.di

import androidx.lifecycle.ViewModel
import org.koin.core.definition.Definition
import org.koin.core.definition.KoinDefinition
import org.koin.core.module.Module
import org.koin.core.qualifier.Qualifier

// https://github.com/InsertKoinIO/koin-annotations/issues/130#issuecomment-2189079092
public inline fun <reified T : ViewModel> Module.viewModel(
qualifier: Qualifier? = null,
noinline definition: Definition<T>,
): KoinDefinition<T> = factory(qualifier, definition)
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.vn.chungha.pet_kmm.domain

import com.vn.chungha.pet_kmm.data.remote.response.PetCatResponse
import com.github.michaelbull.result.Result
import com.vn.chungha.pet_kmm.domain.model.PetModel

interface PetCatRepository {
suspend fun getPetCatByPage(query: String, page: Int, perPage: Int): List<PetCatResponse>
suspend fun getPetCatByPage(query: String, page: Int, perPage: Int): Result<List<PetModel>, Throwable>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import androidx.compose.runtime.Immutable
import com.vn.chungha.pet_kmm.domain.model.PetModel
import kotlinx.collections.immutable.ImmutableList


@Immutable
data class HomePetUiState(
val isLoading : Boolean,
val isFirstPage : Boolean,
val currentPage : Int,
val petList : ImmutableList<PetModel>,
val petList : List<PetModel>,
val error : String
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.vn.chungha.pet_kmm.presentation.home
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import co.touchlab.kermit.Logger
import com.github.michaelbull.result.fold
import com.vn.chungha.pet_kmm.domain.PetCatRepository
import com.vn.chungha.pet_kmm.domain.mapper.toPetModel
import com.vn.chungha.pet_kmm.domain.model.PetModel
Expand All @@ -18,36 +19,39 @@ import org.koin.core.component.inject

open class HomePetViewModel : ViewModel(), KoinComponent {

private val homePetRepository: PetCatRepository by inject()
private val homePetRepository: PetCatRepository by inject()

private val _statePetModel = MutableStateFlow<List<PetModel>>(emptyList<PetModel>())
val statePetModel: StateFlow<List<PetModel>> = _statePetModel
private val _statePetModel = MutableStateFlow<List<PetModel>>(emptyList<PetModel>())
val statePetModel: StateFlow<List<PetModel>> = _statePetModel

val demo = "Hello KMM! 123 213213 13 12321 3"

init {
Logger.d { "HomePetViewModel" }
}
init {
Logger.d { "HomePetViewModel" }
}

private val _messages = MutableStateFlow<String>("Hello")
val messages: StateFlow<String> = _messages
private val _messages = MutableStateFlow<String>("Hello")
val messages: StateFlow<String> = _messages

init {
getPetList()
_messages.value = "Hello KMM!"
}
init {
getPetFirstList()
_messages.value = "Hello KMM!"
}

fun getPetList() {
viewModelScope.launch {
val result = withContext(Dispatchers.IO) {
homePetRepository.getPetCatByPage("breed", 1, 10)
}
_statePetModel.value = result.map { it.toPetModel() }
fun getPetFirstList() {
viewModelScope.launch {
homePetRepository.getPetCatByPage("breed", 1, 10)
.fold(
success = {
_statePetModel.value = it
},
failure = {

}
},
)
}
}

companion object {
const val TAG = "HomePetViewModel"
}
companion object {
const val TAG = "HomePetViewModel"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.vn.chungha.pet_kmm.utils

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.MainCoroutineDispatcher

internal class AppCoroutineDispatcherImpl : AppCoroutineDispatchers {
override val main: CoroutineDispatcher get() = Dispatchers.Main
override val io: CoroutineDispatcher get() = Dispatchers.IO
override val default: CoroutineDispatcher get() = Dispatchers.Default
override val unconfined: CoroutineDispatcher get() = Dispatchers.Unconfined
override val immediateMain: MainCoroutineDispatcher get() = Dispatchers.Main.immediate
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.vn.chungha.pet_kmm.utils

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers

/**
* An interface that provides properties for accessing commonly used [CoroutineDispatcher]s. This differs from the
* [Dispatchers] object in that it has consistent properties across all platforms and since [AppCoroutineDispatchers] is
* an interface, it can easily be mocked and tested, and different implementations can easily be made to adapt to
* different scenarios.
*
* Each supported platform contains an implementation of this [AppCoroutineDispatchers] interface.
*
* Note that not all platforms natively support all of the [CoroutineDispatcher] types (ex: only JVM supports
* Dispatchers.IO), so fallbacks are provided when they aren't available for the default implementations.
*/
interface AppCoroutineDispatchers {
/**
* The companion object for the [CoroutineDispatchers] interface. This is provided so that it's possible to create
* extension functions and properties on the companion object.
*/
companion object

/**
* The main [CoroutineDispatcher] that is usually used for UI work. Default implementations of this interface,
* refer to [Dispatchers.Main] for this value when it is available.
*
* Note that this isn't available on all platforms, so when one isn't present, this falls back to the [default]
* [CoroutineDispatcher] in the default implementation.
*
* Default implementation [CoroutineDispatcher]:
* Android - Main
* iOS - Custom Main implementation or Default
*/
val main: CoroutineDispatcher

/**
* The [CoroutineDispatcher] that is usually used for input/output, or intensive operations. Default
* implementations of this interface, refer to Dispatchers.IO for this value when it is available.
*
* Note that this isn't available on all platforms, so when one isn't present, this falls back to the [default]
* [CoroutineDispatcher] in the default implementation.
*
* Default implementation [CoroutineDispatcher]:
* Android - IO
* iOS - Default
*/
val io: CoroutineDispatcher

/**
* The [CoroutineDispatcher] that is the default that is used by all standard builders like launch and async if no
* other [CoroutineDispatcher] is provided or in their context. Default implementations of this interface, refer to
* [Dispatchers.Default] for this value.
*
* Default implementation [CoroutineDispatcher]:
* Android - Default
* iOS - Default
*/
val default: CoroutineDispatcher

/**
* The [CoroutineDispatcher] that is not confined to any specific thread. Default implementations of this
* interface refer to [Dispatchers.Unconfined] for this value.
*
* Default implementation [CoroutineDispatcher]:
* Android - Unconfined
* iOS - Unconfined
*/
val unconfined: CoroutineDispatcher

/**
* Returns dispatcher that executes coroutines immediately when it is already in the right context
* (e.g. current looper is the same as this handler's looper) without an additional [re-dispatch][CoroutineDispatcher.dispatch].
*
* Immediate dispatcher is safe from stack overflows and in case of nested invocations forms event-loop similar to [Dispatchers.Unconfined].
* The event loop is an advanced topic and its implications can be found in [Dispatchers.Unconfined] documentation.
* The formed event-loop is shared with [Unconfined] and other immediate dispatchers, potentially overlapping tasks between them.
*
* Example of usage:
* ```
* suspend fun updateUiElement(val text: String) {
* /*
* * If it is known that updateUiElement can be invoked both from the Main thread and from other threads,
* * `immediate` dispatcher is used as a performance optimization to avoid unnecessary dispatch.
* *
* * In that case, when `updateUiElement` is invoked from the Main thread, `uiElement.text` will be
* * invoked immediately without any dispatching, otherwise, the `Dispatchers.Main` dispatch cycle will be triggered.
* */
* withContext(immediateMain) {
* uiElement.text = text
* }
* // Do context-independent logic such as logging
* }
* ```
*
* Method may throw [UnsupportedOperationException] if immediate dispatching is not supported by current dispatcher,
* please refer to specific dispatcher documentation.
*
* [Dispatchers.Main] supports immediate execution for Android, JavaFx and Swing platforms.
*/
val immediateMain: CoroutineDispatcher
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.vn.chungha.pet_kmm.utils

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob

interface AppCoroutineScope : CoroutineScope

internal open class IoAppCoroutineScopeImpl(appCoroutineDispatchers: AppCoroutineDispatchers) : AppCoroutineScope {
override val coroutineContext = SupervisorJob() + appCoroutineDispatchers.io

override fun toString() = "DefaultAppCoroutineScope(coroutineContext=$coroutineContext)"
}

internal open class MainAppCoroutineScopeImpl(appCoroutineDispatchers: AppCoroutineDispatchers) : AppCoroutineScope {
override val coroutineContext = SupervisorJob() + appCoroutineDispatchers.main

override fun toString() = "DefaultAppCoroutineScope(coroutineContext=$coroutineContext)"
}
6 changes: 5 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ androidxComposeBom = "2024.06.00"


build-konfig = "0.15.1"
kotlinResult = "2.0.0"
kotlinResultCoroutines = "2.0.0"
ksp = "2.0.0-1.0.21"
kotlinx-serialization = "1.7.0"

Expand Down Expand Up @@ -50,6 +52,8 @@ junit = "4.13.2"
kermit = "2.0.3"

[libraries]
kotlin-result = { module = "com.michael-bull.kotlin-result:kotlin-result", version.ref = "kotlinResult" }
kotlin-result-coroutines = { module = "com.michael-bull.kotlin-result:kotlin-result-coroutines", version.ref = "kotlinResultCoroutines" }
kotlinx-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinx-dateTime" }
kotlinx-serialization = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core", version.ref = "kotlinx-serialization" }
Expand Down Expand Up @@ -145,4 +149,4 @@ ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
gradle-spotless = { id = "com.diffplug.gradle.spotless", version.ref = "spotless" }
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
kotlinx-kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kotlinx-kover" }
buildKonfig = { id = "com.codingfeline.buildkonfig", version = "0.15.1" }
buildKonfig = { id = "com.codingfeline.buildkonfig", version = "0.15.1" }
4 changes: 2 additions & 2 deletions iosApp/Pods/Pods.xcodeproj/project.pbxproj

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit e9fda80

Please sign in to comment.