Skip to content

Conversation

Copilot
Copy link

@Copilot Copilot AI commented Sep 27, 2025

This PR implements the selectAsStateFlow API that combines FlowExt's powerful memoized selector functionality with StateFlow conversion, addressing the investigation request in the original issue.

Overview

The selectAsStateFlow functions provide a seamless way to create hot, stateful flows from memoized selectors, making them perfect for UI state management scenarios where you need both computational efficiency and immediate value access.

Key Features

  • Memoized Computation: Leverages existing select() functions to ensure selectors are only recomputed when input sub-states actually change
  • Hot StateFlow: Converts the selected Flow to a StateFlow using stateIn(), providing immediate access to the latest value via .value
  • Configurable Sharing: Supports all SharingStarted strategies (Eagerly, Lazily, WhileSubscribed) for fine-grained control over StateFlow lifecycle
  • Multiple Overloads: Complete API coverage with 1-5 selector variants, matching the existing select() function signatures

Example Usage

val scope = CoroutineScope(Dispatchers.Default)

val uiStateFlow = MutableStateFlow(
  UiState(
    items = listOf("apple", "banana", "cherry"),
    searchTerm = "a",
    isLoading = false
  )
)

// Create a StateFlow that efficiently filters items based on search term
val filteredItemsStateFlow = uiStateFlow.selectAsStateFlow(
  scope = scope,
  started = SharingStarted.Eagerly,
  initialValue = emptyList<String>(),
  selector1 = { it.items },
  selector2 = { it.searchTerm },
  projector = { items, term ->
    items.filter { it.contains(term ?: "", ignoreCase = true) }
  }
)

// Immediate access to current value
println("Current items: ${filteredItemsStateFlow.value}")

// Collect updates as usual
filteredItemsStateFlow.collect { items ->
  updateUI(items)
}

Benefits Over Regular select()

  • Immediate Value Access: No need to collect the flow to get the current state
  • Hot Sharing: Multiple collectors share the same StateFlow instance
  • UI-Friendly: Perfect for Android ViewModels and Compose state management
  • Lifecycle Aware: Configurable sharing strategies for optimal resource management

Implementation Details

The implementation is built on top of the existing select() functions and uses Kotlin's stateIn() operator for StateFlow conversion. This ensures:

  • Full compatibility with existing FlowExt patterns
  • Minimal code duplication
  • Consistent behavior with memoized selectors
  • Support for all Kotlin Multiplatform targets

Testing

Comprehensive test suite covering:

  • All 5 function variants (1-5 selectors)
  • StateFlow behavior and immediate value access
  • Memoization efficiency
  • Different sharing strategies
  • Both MutableStateFlow and Flow sources

Documentation

  • Updated README.md with detailed examples and use cases
  • Complete inline API documentation
  • Added to table of contents for easy discovery

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.foojay.io
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -XX:&#43;UseParallelGC -Xmx4g -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-9.0.0-bin/d6wjpkvcgsg3oed0qlfss3wgl/gradle-9.0.0/lib/gradle-daemon-main-9.0.0.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-9.0.0-bin/d6wjpkvcgsg3oed0qlfss3wgl/gradle-9.0.0/lib/agents/gradle-instrumentation-agent-9.0.0.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 9.0.0 (dns block)
  • download.jetbrains.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -XX:&#43;UseParallelGC -Xmx4g -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-9.0.0-bin/d6wjpkvcgsg3oed0qlfss3wgl/gradle-9.0.0/lib/gradle-daemon-main-9.0.0.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-9.0.0-bin/d6wjpkvcgsg3oed0qlfss3wgl/gradle-9.0.0/lib/agents/gradle-instrumentation-agent-9.0.0.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 9.0.0 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>selectAsStateFlow investigation 🙏</issue_title>
<issue_description>🙏

Sent from my 2201117TG using FastHub</issue_description>

<agent_instructions>unit tests, docs, and readme.md</agent_instructions>

Comments on the Issue (you are @copilot in this section)

Fixes #150

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Co-authored-by: hoc081098 <36917223+hoc081098@users.noreply.github.com>
@Copilot Copilot AI changed the title [WIP] selectAsStateFlow investigation 🙏 Implement selectAsStateFlow API for memoized state selection with StateFlow conversion Sep 27, 2025
@Copilot Copilot AI requested a review from hoc081098 September 27, 2025 09:01
Copilot finished work on behalf of hoc081098 September 27, 2025 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

selectAsStateFlow investigation 🙏

2 participants