index.html 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. <!DOCTYPE html>
  2. <html>
  3. <head><script src="/livereload.js?mindelay=10&amp;v=2&amp;port=1313&amp;path=livereload" data-no-instant defer></script>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width,minimum-scale=1">
  6. <title>MVI Architecture Helper | codeskraps</title>
  7. <link rel="canonical" href="http://localhost:1313/posts/mvi_architecture/">
  8. <meta name="description" content="Something smart to talk about this helper class
  9. interface StateReceiver&lt;STATE&gt; {
  10. suspend fun updateState(transform: suspend (STATE) -&gt; STATE)
  11. suspend fun withState(block: suspend (STATE) -&gt; Unit)
  12. }
  13. suspend inline fun &lt;reified TYPE : STATE, STATE&gt; StateReceiver&lt;STATE&gt;.withType(crossinline block: suspend (TYPE) -&gt; Unit) {
  14. withState { state -&gt;
  15. if (state is TYPE) {
  16. block(state)
  17. }
  18. }
  19. }
  20. suspend inline fun &lt;reified TYPE : STATE, STATE&gt; StateReceiver&lt;STATE&gt;.updateWithType(crossinline transform: suspend (TYPE) -&gt; TYPE) {
  21. withType&lt;TYPE, STATE&gt; { state -&gt; updateState { transform(state) } }
  22. }
  23. interface StateProvider&lt;STATE&gt; {
  24. val state: StateFlow&lt;STATE&gt;
  25. }
  26. interface IntentReceiver&lt;INTENT&gt; {
  27. fun handleIntent(intent: INTENT)
  28. }
  29. interface ActionProvider&lt;ACTION&gt; {
  30. val action: Flow&lt;ACTION&gt;
  31. }
  32. interface ActionReceiver&lt;ACTION&gt; {
  33. suspend fun sendAction(block: suspend () -&gt; ACTION)
  34. }
  35. interface StateModule&lt;STATE&gt; : StateReceiver&lt;STATE&gt;, StateProvider&lt;STATE&gt;
  36. interface IntentModule&lt;INTENT&gt; : IntentReceiver&lt;INTENT&gt;
  37. interface ActionModule&lt;ACTION&gt; : ActionReceiver&lt;ACTION&gt;, ActionProvider&lt;ACTION&gt;
  38. interface MVIViewModel&lt;STATE, INTENT, ACTION&gt; :
  39. StateModule&lt;STATE&gt;,
  40. IntentModule&lt;INTENT&gt;,
  41. ActionModule&lt;ACTION&gt;
  42. class MVIViewModelDelegate&lt;STATE, INTENT, ACTION&gt;(
  43. initial: STATE
  44. ) : MVIViewModel&lt;STATE, INTENT, ACTION&gt; {
  45. private val _state = MutableStateFlow(initial)
  46. override val state: StateFlow&lt;STATE&gt; = _state.asStateFlow()
  47. private val _action = Channel&lt;ACTION&gt;()
  48. override val action: Flow&lt;ACTION&gt; = _action.receiveAsFlow()
  49. override suspend fun updateState(transform: suspend (STATE) -&gt; STATE) {
  50. _state.update { transform(it) }
  51. }
  52. override suspend fun withState(block: suspend (STATE) -&gt; Unit) {
  53. block(_state.value)
  54. }
  55. override suspend fun sendAction(block: suspend () -&gt; ACTION) {
  56. _action.trySend(block())
  57. }
  58. override fun handleIntent(intent: INTENT) {
  59. throw NotImplementedError()
  60. }
  61. }" />
  62. <meta property="og:type" content="article" />
  63. <meta property="og:title" content="MVI Architecture Helper | codeskraps" />
  64. <meta property="og:url" content="http://localhost:1313/posts/mvi_architecture/" />
  65. <meta property="og:description" content="Something smart to talk about this helper class
  66. interface StateReceiver&lt;STATE&gt; {
  67. suspend fun updateState(transform: suspend (STATE) -&gt; STATE)
  68. suspend fun withState(block: suspend (STATE) -&gt; Unit)
  69. }
  70. suspend inline fun &lt;reified TYPE : STATE, STATE&gt; StateReceiver&lt;STATE&gt;.withType(crossinline block: suspend (TYPE) -&gt; Unit) {
  71. withState { state -&gt;
  72. if (state is TYPE) {
  73. block(state)
  74. }
  75. }
  76. }
  77. suspend inline fun &lt;reified TYPE : STATE, STATE&gt; StateReceiver&lt;STATE&gt;.updateWithType(crossinline transform: suspend (TYPE) -&gt; TYPE) {
  78. withType&lt;TYPE, STATE&gt; { state -&gt; updateState { transform(state) } }
  79. }
  80. interface StateProvider&lt;STATE&gt; {
  81. val state: StateFlow&lt;STATE&gt;
  82. }
  83. interface IntentReceiver&lt;INTENT&gt; {
  84. fun handleIntent(intent: INTENT)
  85. }
  86. interface ActionProvider&lt;ACTION&gt; {
  87. val action: Flow&lt;ACTION&gt;
  88. }
  89. interface ActionReceiver&lt;ACTION&gt; {
  90. suspend fun sendAction(block: suspend () -&gt; ACTION)
  91. }
  92. interface StateModule&lt;STATE&gt; : StateReceiver&lt;STATE&gt;, StateProvider&lt;STATE&gt;
  93. interface IntentModule&lt;INTENT&gt; : IntentReceiver&lt;INTENT&gt;
  94. interface ActionModule&lt;ACTION&gt; : ActionReceiver&lt;ACTION&gt;, ActionProvider&lt;ACTION&gt;
  95. interface MVIViewModel&lt;STATE, INTENT, ACTION&gt; :
  96. StateModule&lt;STATE&gt;,
  97. IntentModule&lt;INTENT&gt;,
  98. ActionModule&lt;ACTION&gt;
  99. class MVIViewModelDelegate&lt;STATE, INTENT, ACTION&gt;(
  100. initial: STATE
  101. ) : MVIViewModel&lt;STATE, INTENT, ACTION&gt; {
  102. private val _state = MutableStateFlow(initial)
  103. override val state: StateFlow&lt;STATE&gt; = _state.asStateFlow()
  104. private val _action = Channel&lt;ACTION&gt;()
  105. override val action: Flow&lt;ACTION&gt; = _action.receiveAsFlow()
  106. override suspend fun updateState(transform: suspend (STATE) -&gt; STATE) {
  107. _state.update { transform(it) }
  108. }
  109. override suspend fun withState(block: suspend (STATE) -&gt; Unit) {
  110. block(_state.value)
  111. }
  112. override suspend fun sendAction(block: suspend () -&gt; ACTION) {
  113. _action.trySend(block())
  114. }
  115. override fun handleIntent(intent: INTENT) {
  116. throw NotImplementedError()
  117. }
  118. }" />
  119. <link rel="stylesheet" href="/lib/icofont/icofont.min.css" />
  120. <link rel="stylesheet" href="/css/syntax.css" />
  121. <link rel="stylesheet" href="/css/style.css" />
  122. <script src="/js/copy-code-block.js"></script>
  123. <link rel="shortcut icon" href="/images/favicon.ico" type="image/x-icon" />
  124. </head>
  125. <body>
  126. <header class="header-wrapper">
  127. <div class="header">
  128. <a class="site-title" href="http://localhost:1313/">codeskraps</a>
  129. <nav class="menu">
  130. <div class="menu-item">
  131. <a href="/about/">About</a>
  132. </div>
  133. <div class="menu-item">
  134. <a href="/posts/">Posts</a>
  135. </div>
  136. </nav>
  137. </div>
  138. </header>
  139. <main class="main-wrapper">
  140. <div class="main">
  141. <section class="single">
  142. <h1 class="title">MVI Architecture Helper</h1>
  143. <div class="tip">
  144. <time datetime="2024-09-27 13:45:09 &#43;0200 CEST">2024/09/27</time>
  145. <span class="split">·</span>
  146. <span> 213 words </span>
  147. <span class="split">·</span>
  148. <span>
  149. 1 minutes to read
  150. </span>
  151. </div>
  152. <div class="taxonomies">
  153. </div>
  154. <hr />
  155. <div class="content">
  156. <p>Something smart to talk about this helper class</p>
  157. <div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">StateReceiver</span>&lt;STATE&gt; {
  158. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">suspend</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">updateState</span>(transform: <span style="color:#66d9ef">suspend</span> (STATE) <span style="color:#f92672">-&gt;</span> STATE)
  159. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">suspend</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">withState</span>(block: <span style="color:#66d9ef">suspend</span> (STATE) <span style="color:#f92672">-&gt;</span> Unit)
  160. </span></span><span style="display:flex;"><span>}
  161. </span></span><span style="display:flex;"><span>
  162. </span></span><span style="display:flex;"><span><span style="color:#66d9ef">suspend</span> <span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">fun</span> &lt;<span style="color:#66d9ef">reified</span> <span style="color:#a6e22e">TYPE</span> : <span style="color:#a6e22e">STATE</span>, <span style="color:#a6e22e">STATE</span>&gt; <span style="color:#a6e22e">StateReceiver</span>&lt;STATE&gt;.withType(<span style="color:#66d9ef">crossinline</span> block: <span style="color:#66d9ef">suspend</span> (TYPE) <span style="color:#f92672">-&gt;</span> Unit) {
  163. </span></span><span style="display:flex;"><span> withState { state <span style="color:#f92672">-&gt;</span>
  164. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (state <span style="color:#66d9ef">is</span> TYPE) {
  165. </span></span><span style="display:flex;"><span> block(state)
  166. </span></span><span style="display:flex;"><span> }
  167. </span></span><span style="display:flex;"><span> }
  168. </span></span><span style="display:flex;"><span>}
  169. </span></span><span style="display:flex;"><span>
  170. </span></span><span style="display:flex;"><span><span style="color:#66d9ef">suspend</span> <span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">fun</span> &lt;<span style="color:#66d9ef">reified</span> <span style="color:#a6e22e">TYPE</span> : <span style="color:#a6e22e">STATE</span>, <span style="color:#a6e22e">STATE</span>&gt; <span style="color:#a6e22e">StateReceiver</span>&lt;STATE&gt;.updateWithType(<span style="color:#66d9ef">crossinline</span> transform: <span style="color:#66d9ef">suspend</span> (TYPE) <span style="color:#f92672">-&gt;</span> TYPE) {
  171. </span></span><span style="display:flex;"><span> withType&lt;TYPE, STATE&gt; { state <span style="color:#f92672">-&gt;</span> updateState { transform(state) } }
  172. </span></span><span style="display:flex;"><span>}
  173. </span></span><span style="display:flex;"><span>
  174. </span></span><span style="display:flex;"><span><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">StateProvider</span>&lt;STATE&gt; {
  175. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">val</span> state: StateFlow&lt;STATE&gt;
  176. </span></span><span style="display:flex;"><span>}
  177. </span></span><span style="display:flex;"><span>
  178. </span></span><span style="display:flex;"><span><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">IntentReceiver</span>&lt;INTENT&gt; {
  179. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">handleIntent</span>(intent: INTENT)
  180. </span></span><span style="display:flex;"><span>}
  181. </span></span><span style="display:flex;"><span>
  182. </span></span><span style="display:flex;"><span><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">ActionProvider</span>&lt;ACTION&gt; {
  183. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">val</span> action: Flow&lt;ACTION&gt;
  184. </span></span><span style="display:flex;"><span>}
  185. </span></span><span style="display:flex;"><span>
  186. </span></span><span style="display:flex;"><span><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">ActionReceiver</span>&lt;ACTION&gt; {
  187. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">suspend</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">sendAction</span>(block: <span style="color:#66d9ef">suspend</span> () <span style="color:#f92672">-&gt;</span> ACTION)
  188. </span></span><span style="display:flex;"><span>}
  189. </span></span><span style="display:flex;"><span>
  190. </span></span><span style="display:flex;"><span><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">StateModule</span>&lt;STATE&gt; : StateReceiver&lt;STATE&gt;, StateProvider&lt;STATE&gt;
  191. </span></span><span style="display:flex;"><span><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">IntentModule</span>&lt;INTENT&gt; : IntentReceiver&lt;INTENT&gt;
  192. </span></span><span style="display:flex;"><span><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">ActionModule</span>&lt;ACTION&gt; : ActionReceiver&lt;ACTION&gt;, ActionProvider&lt;ACTION&gt;
  193. </span></span><span style="display:flex;"><span>
  194. </span></span><span style="display:flex;"><span><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">MVIViewModel</span>&lt;STATE, INTENT, ACTION&gt; :
  195. </span></span><span style="display:flex;"><span> StateModule&lt;STATE&gt;,
  196. </span></span><span style="display:flex;"><span> IntentModule&lt;INTENT&gt;,
  197. </span></span><span style="display:flex;"><span> ActionModule&lt;ACTION&gt;
  198. </span></span><span style="display:flex;"><span>
  199. </span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MVIViewModelDelegate</span>&lt;STATE, INTENT, ACTION&gt;(
  200. </span></span><span style="display:flex;"><span> initial: STATE
  201. </span></span><span style="display:flex;"><span>) : MVIViewModel&lt;STATE, INTENT, ACTION&gt; {
  202. </span></span><span style="display:flex;"><span>
  203. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">val</span> _state = MutableStateFlow(initial)
  204. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">val</span> state: StateFlow&lt;STATE&gt; = _state.asStateFlow()
  205. </span></span><span style="display:flex;"><span>
  206. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">val</span> _action = Channel&lt;ACTION&gt;()
  207. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">val</span> action: Flow&lt;ACTION&gt; = _action.receiveAsFlow()
  208. </span></span><span style="display:flex;"><span>
  209. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">suspend</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">updateState</span>(transform: <span style="color:#66d9ef">suspend</span> (STATE) <span style="color:#f92672">-&gt;</span> STATE) {
  210. </span></span><span style="display:flex;"><span> _state.update { transform(<span style="color:#66d9ef">it</span>) }
  211. </span></span><span style="display:flex;"><span> }
  212. </span></span><span style="display:flex;"><span>
  213. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">suspend</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">withState</span>(block: <span style="color:#66d9ef">suspend</span> (STATE) <span style="color:#f92672">-&gt;</span> Unit) {
  214. </span></span><span style="display:flex;"><span> block(_state.<span style="color:#66d9ef">value</span>)
  215. </span></span><span style="display:flex;"><span> }
  216. </span></span><span style="display:flex;"><span>
  217. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">suspend</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">sendAction</span>(block: <span style="color:#66d9ef">suspend</span> () <span style="color:#f92672">-&gt;</span> ACTION) {
  218. </span></span><span style="display:flex;"><span> _action.trySend(block())
  219. </span></span><span style="display:flex;"><span> }
  220. </span></span><span style="display:flex;"><span>
  221. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">handleIntent</span>(intent: INTENT) {
  222. </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> NotImplementedError()
  223. </span></span><span style="display:flex;"><span> }
  224. </span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
  225. </div>
  226. </section>
  227. </div>
  228. <div class="side">
  229. <div class="side-recent">
  230. <h2 class="side-title">
  231. <a href="/posts/">Recent Posts</a>
  232. </h2>
  233. <hr />
  234. <ul>
  235. <li>
  236. <a href="/posts/mvi_architecture/">MVI Architecture Helper</a>
  237. </li>
  238. <li>
  239. <a href="/posts/my-first-post/">My First Post</a>
  240. </li>
  241. </ul>
  242. </div>
  243. <div class="side-categories">
  244. <h2>Categories</h2>
  245. <hr />
  246. <ul>
  247. </ul>
  248. </div>
  249. <div class="side-tags">
  250. <h2>Tags</h2>
  251. <hr />
  252. <ul>
  253. </ul>
  254. </div>
  255. </div>
  256. </main>
  257. <footer class="footer">
  258. <div class="footer-row">
  259. <a class="footer-item" href="http://localhost:1313/posts/index.xml">
  260. Feed of Posts
  261. <i class="icofont-rss"></i>
  262. </a>
  263. </div>
  264. </footer>
  265. </body>
  266. </html>