Skip to content

Instantly share code, notes, and snippets.

@fabriciovergara
Last active February 16, 2024 22:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fabriciovergara/38fb7d33daec404d1f770da88f95aa8b to your computer and use it in GitHub Desktop.
Save fabriciovergara/38fb7d33daec404d1f770da88f95aa8b to your computer and use it in GitHub Desktop.
Compose KMP For Result
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.SaverScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.snapshots.SnapshotStateMap
import cafe.adriel.voyager.core.screen.Screen
import core.platform.PlatformParcelable
import core.platform.PlatformParcelize
import core.platform.PlatformRawValue
import core.kotlin.UUID
import core.platform.PlatformIgnoredOnParcel
import kotlinx.serialization.Serializable
import kotlin.native.HiddenFromObjC
private val LocalNavResultStateHolder = compositionLocalOf<SnapshotStateMap<String, NavResult<*>>> {
throw AssertionError()
}
private object StateMapSaver :
Saver<SnapshotStateMap<String, NavResult<*>>, List<StateMapSaver.KeyValue>> {
@PlatformParcelize
class KeyValue(val key: String, val value: NavResult<*>) : PlatformParcelable
override fun restore(
value: List<KeyValue>
): SnapshotStateMap<String, NavResult<*>>? {
return mutableStateMapOf(*value.map { it.key to it.value }.toTypedArray())
}
override fun SaverScope.save(
value: SnapshotStateMap<String, NavResult<*>>
): List<KeyValue> {
return value.entries.map { KeyValue(it.key, it.value) }
}
}
@Composable
internal fun ProvideNavResultStateHolder(
content: @Composable () -> Unit
) {
val stateHolder = rememberSaveable(
saver = StateMapSaver
) {
mutableStateMapOf()
}
CompositionLocalProvider(
LocalNavResultStateHolder provides stateHolder,
content = content
)
}
@HiddenFromObjC
internal sealed class NavResult<out T> : PlatformParcelable {
@HiddenFromObjC
@PlatformParcelize
data class Value<T>(val value: @PlatformRawValue T) : NavResult<T>()
@HiddenFromObjC
@PlatformParcelize
data object Empty : NavResult<Nothing>()
}
@Stable
@Serializable
@PlatformParcelize
internal class NavResultRequest<T>(
val key: String
) : PlatformParcelable
@Stable
internal class NavResultRequestContract<T>(
private val key: String,
private val stateHolder: SnapshotStateMap<String, NavResult<*>>,
) {
val request: NavResultRequest<T> = NavResultRequest(key)
val response: State<NavResult<T>> = derivedStateOf {
val currentValue = stateHolder[key]
if (currentValue != null && currentValue is NavResult.Value<*>) {
@Suppress("UNCHECKED_CAST")
currentValue as NavResult<T>
} else {
NavResult.Empty
}
}
fun consume() {
stateHolder.remove(key)
}
}
@Stable
internal class NavResultResponseContract<T>(
private val key: String,
private val stateHolder: SnapshotStateMap<String, NavResult<*>>,
) {
fun send(value: T) {
stateHolder[key] = NavResult.Value(value)
}
}
@Composable
internal fun <T> rememberNavResultContract(
request: NavResultRequest<T>
): NavResultResponseContract<T> {
val stateHolder = LocalNavResultStateHolder.current
return remember(stateHolder, request.key) {
NavResultResponseContract(
key = request.key,
stateHolder = stateHolder
)
}
}
@Composable
internal fun <T> rememberNavResultContract(
key: String
): NavResultRequestContract<T> {
val stateHolder = LocalNavResultStateHolder.current
val contractKey = rememberSaveable { "$key+${UUID.randomUUID()}" }
return remember(stateHolder, key) {
NavResultRequestContract(
key = contractKey,
stateHolder = stateHolder
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment