Skip to content
/ loomIn Public

πŸš€ Loom: A Metaphor for Dynamic State Management, The name Loom draws inspiration from the traditional tool used for weaving fabric, symbolizing the process of integrating and managing data streams seamlessly in modern applications. This analogy is especially fitting for a system responsible

License

Notifications You must be signed in to change notification settings

nphausg/loomIn

Repository files navigation

Loom: A Metaphor for Dynamic State Management

PRs welcome!
The name Loom draws inspiration from the traditional tool used for weaving fabric, symbolizing the process of integrating and managing data streams seamlessly in modern applications. This analogy is especially fitting for a system responsible for handling dynamic state updates, like refreshing UI states or fetching new data.

-----------------------------------------------------

nphausg

πŸ‘‡ Overview

🎯 Why LoomIn?

LoomIn offers a clean, easy-to-use solution for developers handling complex asynchronous workflows. It simplifies managing state-driven operations and ensures your app performs efficiently by avoiding redundant calls and state updates.

  • Integration: A loom weaves threads into a cohesive fabric, much like how the Loom system integrates multiple data streams into a single, unified state.

  • Precision and Control: Just as a loom controls the pattern and flow of threads, the Loom manages the timing and structure of state updates, ensuring consistency and avoiding unnecessary re-fetches.

  • Scalability: Looms can handle complex patterns with many threads, mirroring how the Loom system supports diverse and complex state flows with ease.

πŸ› Loom in State Management

The Loom acts as an orchestrator for state updates, focusing on two key aspects:

  • Initial Data Fetch On the first subscription, Loom triggers the fetch operation to populate the state with initial data. Example: Automatically loading the home screen data on app launch.

  • Controlled Refreshing Subsequent refresh actions are explicitly triggered by user interactions or external signals, ensuring efficient and intentional updates without redundant API calls.

πŸš€ Key Features of Loom

  • Debounce and Throttle: Control the frequency of state updates with customizable debounce and throttle intervals.

  • StateFlow Management: Efficiently manage state with StateFlow, offering a seamless reactive flow system.

  • Safe Concurrency: Synchronize tasks using a robust locking mechanism to avoid race conditions and ensure thread safety.

  • Kotlin-centric: Built with Kotlin and CoroutineScope, leveraging Kotlin's best features for asynchronous operations.

  • Replay-Capable Flow The Loom uses a replayable StateFlow to retain the last emitted value, ensuring late subscribers can still access the most recent state without re-fetching data.

  • Explicit Refresh Triggers Allows the UI or external components to trigger updates through a refresh() function, maintaining clear separation of responsibilities.

  • Seamless Integration Works with Kotlin's StateFlow or SharedFlow to provide reactive and declarative state management.

πŸš€ Advantages of Loom

  • Efficiency

    • Avoids unnecessary recomposition or redundant API calls by reusing existing data when possible.
    • Only the first subscriber triggers the initial data fetch, optimizing network usage.
  • Reusability

    • Can be used across multiple ViewModels or components for consistent state handling.
  • Extensibility

    • Supports adding additional layers (e.g., caching, error handling) without changing its core design.

Implementation

internal class LoomImpl<T>(
  private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO),
  private val debounceTimeMillis: Long = DEBOUNCE,
  private val throttleTimeMillis: Long = THROTTLE,
  private val execute: suspend (Boolean) -> T
) : Loom<T> {
  private val mutex = Mutex();
  private val _modifier = MutableSharedFlow<(T) -> T>(extraBufferCapacity = 1)
  private val _state = MutableSharedFlow<LoomSignal>(
    replay = 0,
    extraBufferCapacity = 1,
    onBufferOverflow = BufferOverflow.DROP_OLDEST
  )
  override val state: StateFlow<LoomState<T>> = _state
    .streamLine(debounceTimeMillis, throttleTimeMillis)
    .onStart { emit(LoomSignal.Automatic) } // Emit the first refresh automatically
    .flatMapLatest { event ->
      flow {
        emit(LoomState.Loading)
        try {
          val data = execute(event is LoomSignal.FromUser)
          emit(LoomState.Loaded(data))
        } catch (e: CancellationException) {
          throw e
        } catch (e: Exception) {
          emit(LoomState.Error(e))
        }
      }
    }
    .combine(_modifier.onStart { emit { data: T -> data } }) { oldState, modifier ->
      mutex.withLock {
        if (oldState is LoomState.Loaded) {
          val expectedData = modifier(oldState.data)
          if (expectedData != oldState.data) {
            LoomState.Loaded(expectedData)
          } else {
            oldState // Don't do anything if value is not changing
          }
        } else {
          oldState // Don't do anything if value is not changing
        }
      }
    }
    .catch { emit(LoomState.Error(it)) }
    .stateIn(
      scope = scope,
      started = SharingStarted.WhileSubscribed(
        stopTimeoutMillis = 5_000,
        replayExpirationMillis = 0
      ),
      initialValue = LoomState.Init
    )

  override fun refresh(fromUser: Boolean) {
    _state.tryEmit(if (fromUser) LoomSignal.FromUser else LoomSignal.Automatic)
  }

  override fun update(modifier: (T) -> T) {
    _modifier.tryEmit(modifier)
  }
}

Conclusion

The Loom metaphor emphasizes the importance of structured, efficient, and scalable state management. By weaving together streams of data and providing explicit controls for refreshing, Loom ensures that the UI remains responsive, consistent, and performant. This approach not only aligns with modern reactive programming paradigms but also provides a clear and elegant mechanism for managing dynamic states.

πŸš€ How to use

  • Step 1: Grab from Maven central at the coordinates:
repositories {
  google()
  mavenCentral()
  maven {
    url = uri("https://maven.pkg.github.com/nphausg/loomIn")
  }
}
  • Step 2: Implementation from your module
$latestVersion = "0.0.1-alpha"
implementation("com.nphausg:loom:$latestVersion")

πŸ“ Contributing

Contributing We welcome contributions! If you're interested in contributing to LoomIn, here are some ways you can help:

  • Fix bugs πŸ›
  • Improve documentation πŸ“š
  • Enhance features πŸ’‘
  • Add examples πŸ–₯️

✨ How to Contribute

  • Fork the repo.
  • Clone your forked repo locally.
  • Create a new branch for your fix or feature.
  • Write tests for your changes (if applicable).
  • Submit a pull request with a detailed explanation of your changes.
  • Please feel free to contact me or make a pull request.

🌍 Connect With me

Follow the repository for updates and improvements:

nphausg

πŸ‘₯ Acknowledgements

Thanks to the Kotlin and Coroutine community for their support and contributions. We also want to thank all of our contributors who helped improve LoomIn! πŸ™

✨ Star, Fork, and Share! ✨

If you find LoomIn helpful, please ⭐️ star the repo and πŸ” fork it to use in your own projects. Your support helps others discover the project! Let’s make asynchronous programming easier for everyone! πŸ˜„πŸš€

πŸ‘‡ Author

About

πŸš€ Loom: A Metaphor for Dynamic State Management, The name Loom draws inspiration from the traditional tool used for weaving fabric, symbolizing the process of integrating and managing data streams seamlessly in modern applications. This analogy is especially fitting for a system responsible

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

 

Packages