Been using a pretty solid pattern for components t...
# decompose
m
Been using a pretty solid pattern for components to handle configuration changes. Thoughts? Improvements?
Copy code
interface KeyComponent {
    fun createKey(data: Data)
}

class DefaultKeyComponent private constructor(
    context: ComponentContext,
    retained: RetainedKeyComponent,
    onCreated: (uuid: String) -> Unit,
): KeyComponent by retained {

    constructor(
        context: ComponentContext,
        api: ApiKeys,
        mainContext: CoroutineContext,
        onCreated: (uuid: String) -> Unit,
    ): this(
        context = context,
        retained = context.instanceKeeper.getOrCreate {
            RetainedKeyComponent(api, mainContext)
        },
        onCreated = onCreated,
    )

    init {
        // Can't pass the lambda to navigate to retained component,
        // b/c that'd be a leak, so observe key here between start/stop
        val observer: (List<Key>) -> Unit = observer@ { list ->
            val key = list.firstOrNull() ?: return@observer
            onCreated(key.uuid)
        }
        context.lifecycle.subscribe(
            onStart = { retained.key.subscribe(observer) },
            onStop = { retained.key.unsubscribe(observer) },
        )
    }
}

private class RetainedKeyComponent(
    private val api: ApiKeys,
    mainContext: CoroutineContext,
): KeyComponent, InstanceKeeper.Instance {

    private val scope = retainedScope(mainContext) // extension function on InstanceKeeper.Instance

    // Uses List b/c Value Type cannot be nullable
    private val _key = MutableValue<List<Key>>(emptyList())
    val key: Value<List<Key>> = _keys.toValue() // extension function on MutableValue

    override fun createKey(data: Data) {
        // TODO
    }

    override fun onDestroy() {
        scope.cancel()
    }
}
a
Could you describe the idea in more detail?
m
By using a secondary constructor for
DefaultKeyComponent
, and instantiating the
RetainedKeyComponent
via
instanceKeeper
, you can use the retained key component as a delegate for the
DefaultKeyComponent
's primary constructor. Of coure, you do not want to pass to the retained component any contextual things such as lambdas to do navigation and what not. So it separates out the logic a bit more into properly scoped components (very much like ViewModels).
a
If this works for you, then I see no issues with this patter! Pretty interesting one!
Perhaps, you could use Flow/Reaktive instead of MutableValue<List<Key>>. But that's minor.