Skip to content

Commit

Permalink
Merge branch 'develop' into MKMPT-7-SPM
Browse files Browse the repository at this point in the history
  • Loading branch information
naveenmindera committed Apr 11, 2024
2 parents 4ab5f8f + 2e14eb3 commit 4323065
Show file tree
Hide file tree
Showing 28 changed files with 171 additions and 52 deletions.
1 change: 1 addition & 0 deletions app/shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ private val modules: List<DelegatingProjectDependency> = listOf(
projects.common.coroutinesKtx,
projects.common.domain,
projects.features.launches.di,
projects.features.launches.presentation,
)

kotlin {
Expand Down
8 changes: 7 additions & 1 deletion common/compose-ktx/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ group = "com.mindera.compose.extensions"
version = "1.0.0"

kotlin {
jvm()
androidTarget()
jvm("desktop")
iOS {
iosX64()
iosArm64()
iosSimulatorArm64()
}

sourceSets {
val commonMain by getting {
Expand Down
7 changes: 6 additions & 1 deletion common/viewmodel/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ version = "1.0.0"
kotlin {
androidTarget()
jvm("desktop")
iOS {
iosX64()
iosArm64()
iosSimulatorArm64()
}

sourceSets {
val commonMain by getting {
dependencies {
api(libs.precompose.viewmodel)
implementation(compose.runtime)
implementation(libs.lifecycle.viewmodel)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.mindera

import kotlin.jvm.JvmSuppressWildcards

typealias BundleOfArguments = Map<@JvmSuppressWildcards Any, @JvmSuppressWildcards Any?>
fun BundleOfArguments.getString(key: Any) = this[key]?.toString().orEmpty()
fun BundleOfArguments.getInt(key: Any) = getString(key).toInt()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.mindera.lifecycle

import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import moe.tlaster.precompose.viewmodel.viewModelScope
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext

open class ViewModel: moe.tlaster.precompose.viewmodel.ViewModel()
open class ViewModel: androidx.lifecycle.ViewModel()

fun ViewModel.launch(
context: CoroutineContext = EmptyCoroutineContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ fun <VM : ViewModel> create(
vararg args: Pair<Any, Any?>,
): VM = create(klass, mapOf(pairs = args))

@Composable
fun <VM : ViewModel> viewModel(
klass: KClass<VM>,
bundle: BundleOfArguments? = null,
keys: List<Any?> = emptyList(),
): VM = moe.tlaster.precompose.viewmodel.viewModel(klass, keys) { create(klass, bundle) }

@Composable
fun <VM : ViewModel> viewModel(
klass: KClass<VM>,
vararg args: Pair<Any, Any?>,
keys: List<Any?> = emptyList(),
): VM = viewModel(klass, mapOf(pairs = args), keys)
//@Composable
//fun <VM : ViewModel> viewModel(
// klass: KClass<VM>,
// bundle: BundleOfArguments? = null,
// keys: List<Any?> = emptyList(),
//): VM = moe.tlaster.precompose.viewmodel.viewModel(klass, keys) { create(klass, bundle) }
//
//@Composable
//fun <VM : ViewModel> viewModel(
// klass: KClass<VM>,
// vararg args: Pair<Any, Any?>,
// keys: List<Any?> = emptyList(),
//): VM = viewModel(klass, mapOf(pairs = args), keys)
5 changes: 5 additions & 0 deletions features/launches/composables/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ version = "1.0.0"
kotlin {
androidTarget()
jvm("desktop")
iOS {
iosX64()
iosArm64()
iosSimulatorArm64()
}

sourceSets {
val commonMain by getting {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,34 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.mindera.compose.collections.ImmutableList
import com.mindera.kmpexample.launches.domain.model.CurrencyExchangeResponseItem
import java.util.Locale

@Composable
fun CurrencyExchangeScreen(
modifier: Modifier = Modifier,
launches: ImmutableList<CurrencyExchangeResponseItem>,
currencies: ImmutableList<CurrencyExchangeResponseItem>,
) {
val lazyListState = rememberLazyListState()

if (launches.isNotEmpty()) {
if (currencies.isNotEmpty()) {
LazyColumn(
modifier = modifier.fillMaxWidth(),
state = lazyListState,
) {
itemsIndexed(items = launches[0].rates, key = { index, _ -> index }) { index, launch ->

if (index != 0) {
Divider(thickness = 2.dp)
currencies.forEach {
itemsIndexed(items = it.rates, key = { index, _ -> index }) { index, launch ->
if (index != 0) {
Divider(thickness = 2.dp)
}
Text(it.no)
Text(launch.id)
Text(launch.currency.replaceFirstChar {
if (it.isLowerCase()) it.titlecase() else it.toString()
})
Text(launch.code, color = Color.Blue)
Text(launch.currencyRate.toString(), color = Color.Gray)
}
Text(launch.id)
Text(launch.currency.replaceFirstChar {
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
})
Text(launch.code, color = Color.Blue)
Text(launch.currencyRate.toString(), color = Color.Gray)
}
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ class KtorCurrencyExchangeRemoteSource constructor(
private val client: HttpClient,
) : CurrencyExchangeRemoteSource {


override suspend fun getCurrencyExchange(): List<DomainCurrencyExchangeResponseItem> =
getCurrencyExchange("A").zip(getCurrencyExchange("B")) { listA, listB ->
DomainCurrencyExchangeResponseItem(listA.rates + listB.rates)
}
getCurrencyExchange("A/last/10/")

private suspend fun getCurrencyExchange(table: String): List<DomainCurrencyExchangeResponseItem> =
client.get("$baseUrl/$table")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import com.mindera.uuid

internal fun CurrencyExchangeResponseItem.toDomain() =
com.mindera.kmpexample.launches.domain.model.CurrencyExchangeResponseItem(
table = table,
no = no,
effectiveDate = effectiveDate,
rates = rates.map { it.toDomain() },
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ data class RatesItem(
@Serializable
data class CurrencyExchangeResponseItem(

@SerialName("table")
val table: String,

@SerialName("no")
val no: String,

@SerialName("effectiveDate")
val effectiveDate: String,

@SerialName("rates")
val rates: List<RatesItem>,
)
4 changes: 2 additions & 2 deletions features/launches/di/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ kotlin {
implementation(libs.koin.core)
implementation(libs.ktor.core)
implementation(libs.ktor.content.negotiation)

implementation(projects.features.launches.presentation)
}
}

val androidMain by getting {
dependsOn(commonMain)
dependencies {
implementation(libs.ktor.okhttp)
implementation(projects.features.launches.presentation)

}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.mindera.di

import com.mindera.kmpexample.launches.domain.usecase.GetCurrencyExchangeUseCase
import com.mindera.kmpexample.launches.usecase.GetCurrencyExchangeUseCaseV1
import com.mindera.kmpexample.launches.viewmodel.CurrencyExchangeViewModel
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject

class DIHelper: KoinComponent {
object DIHelper: KoinComponent {
val getGetCurrencyExchangeUseCase: GetCurrencyExchangeUseCase by inject()
val viewModel: CurrencyExchangeViewModel by inject()
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,11 @@ data class RatesItem(

data class CurrencyExchangeResponseItem(

val table: String,

val no: String,

val effectiveDate: String,

val rates: List<RatesItem>,
)
44 changes: 38 additions & 6 deletions features/launches/presentation/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.compose.multiplatform)
alias(libs.plugins.android.library)
}

group = "com.mindera.kmpexample.features.launches.presentation"
version = "1.0.0"

kotlin {
jvm()
androidTarget()
jvm("desktop")
iOS {
iosX64()
iosArm64()
iosSimulatorArm64()
}


sourceSets {
val commonMain by getting {
dependencies {
implementation(compose.foundation)
implementation(compose.runtime)
implementation(compose.ui)
implementation(compose.material3)

implementation(projects.common.domain)
implementation(projects.common.coroutinesKtx)
implementation(projects.common.composeKtx)
Expand All @@ -36,9 +41,36 @@ kotlin {
implementation(libs.ktor.serialization)
implementation(libs.kotlinx.serialization)
implementation(libs.ktor.logging)
implementation(libs.ktor.okhttp)
implementation(libs.koin.core)

}
}

val androidMain by getting {
dependsOn(commonMain)
dependencies {
implementation(compose.foundation)
implementation(compose.runtime)
implementation(compose.ui)
implementation(compose.material3)
implementation(libs.lifecycle.viewmodel.ktx)
}
}

if (iOSEnabled) {
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
dependencies {
implementation(libs.lifecycle.viewmodel)
}
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ fun CurrencyExchangeScene(onBack: (() -> Unit)) {
BackHandler(onBack = onBack)

val viewModel = remember { KoinHelper().viewModel }
val state = viewModel.state.collectAsState()
val state = viewModel.mutableStateFlow.collectAsState()

Column(modifier = Modifier.fillMaxSize()) {
Text(
text = "Currency Exchange",
fontSize = 40.sp,
)
CurrencyExchangeScreen(
launches = state.value.launches
currencies = state.value.launches
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,51 @@ import com.mindera.kmpexample.launches.domain.model.CurrencyExchangeResponseItem
import com.mindera.kmpexample.launches.domain.usecase.GetCurrencyExchangeUseCase
import com.mindera.lifecycle.ViewModel
import com.mindera.lifecycle.launch
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

class CurrencyExchangeViewModel constructor(
private val getCurrencyExchange: GetCurrencyExchangeUseCase,
) : ViewModel() {
data class State(
data class CurrencyState(
val loading: Boolean = true,
val launches: ImmutableList<CurrencyExchangeResponseItem> = ImmutableList(),
val error: Error? = null,
)

var state = MutableStateFlow(value = State())
var mutableStateFlow = MutableStateFlow(value = CurrencyState())
private set

val currencyStateFlow: StateFlow<CurrencyState>
get() = mutableStateFlow

fun onChange(provideNewState: ((CurrencyState) -> Unit)) {
currencyStateFlow.onEach {
provideNewState.invoke(it)
}.launchIn(CoroutineScope(Dispatchers.Main))
}

// This example to access data in IOS
// class AppViewModel: ObservableObject {
// let coreModel : CurrencyExchangeViewModel = DIHelper().viewModel
//
// func fetchData() {
// coreModel.onChange { newState in
// print(newState.launches)
// }
// }
// }

init {
launch {
getCurrencyExchange().on(
left = {
println(">>> Rates: $it")
state.value = with(state.value) {
mutableStateFlow.value = with(mutableStateFlow.value) {
copy(
loading = false,
launches = it.immutable(),
Expand All @@ -37,7 +62,7 @@ class CurrencyExchangeViewModel constructor(
},
right = {
println(">>> Rates error: $it")
state.value = with(state.value) {
mutableStateFlow.value = with(mutableStateFlow.value) {
copy(
loading = false,
launches = ImmutableList(emptyList()),
Expand Down
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ kotlin.native.binary.memoryModel=experimental
kotlin.native.binary.objcExportSuspendFunctionLaunchThreadRestriction=none
kotlin.native.disableCompilerDaemon=true
kotlin.native.ignoreDisabledTargets=true

org.jetbrains.compose.experimental.uikit.enabled=true
Loading

0 comments on commit 4323065

Please sign in to comment.