Skip to content

Commit 1b398da

Browse files
committed
Explain "data flowing"
1 parent 0ceb514 commit 1b398da

File tree

1 file changed

+79
-1
lines changed

1 file changed

+79
-1
lines changed

ARCHITECTURE.md

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,85 @@ It contains:
139139

140140
#### Data `Flow`ing through functions
141141

142-
TODO (see AutosuggestViewModel)
142+
The place autosuggestion feature makes heavy use of kotlin `Flow`s. Here is the code in `AutosuggestViewModel`:
143+
144+
```kotlin
145+
init {
146+
queryFlow
147+
// 0. Prepare and filter query
148+
.debounce(1000)
149+
.map { it.value }
150+
.filterNotNull()
151+
// 1. Initial state and post
152+
.map { query -> pairQueryToInitialOp(query) }
153+
.flowOn(Dispatchers.Default)
154+
.onEach { (_, initialOp) -> maybePostEmptyState(initialOp) }
155+
.flowOn(Dispatchers.Main)
156+
// 2. State at start (loading) and post
157+
.filterNot { (query, _) -> query.isBlank() }
158+
.map { (query, _) -> pairQueryToStateAtStart(query) }
159+
.flowOn(Dispatchers.Default)
160+
.onEach { (_, stateAtStart) -> _stateLive.value = stateAtStart }
161+
.flowOn(Dispatchers.Main)
162+
// 3. Fetch suggestions
163+
.map { (query, _) -> fetchPlaces(query) }
164+
.flowOn(Dispatchers.IO)
165+
// 4. Map to UI model then post final state
166+
.map { result -> result.toUiModel() }
167+
.map { uiModel -> mapToFinalState(uiModel) }
168+
.flowOn(Dispatchers.Default)
169+
.onEach { newState -> _stateLive.value = newState }
170+
.flowOn(Dispatchers.Main)
171+
.launchIn(viewModelScope)
172+
}
173+
```
174+
175+
It is a composition of 5 processing steps implemented as Kotlin functions:
176+
177+
0. Query preparation
178+
- Debounce queries emitted "upstream" with a timeout of 1000 msec
179+
- Extract the `Query.value` attribute with a `map`
180+
- Exclude `null` values
181+
1. Initial state
182+
- In `pairQueryToInitialOp`, prepare initial operation which can be "post empty state" or "pass", according to the query and current state
183+
- Dispatch upstream processing (step 0 to here) to a computing thread (`Dispatchers.Default`)
184+
- Post the initial operation as a new state if appropriate
185+
- Dispatch upstream processing (from previous dispatch to here) to the main thread (because posting the initial operation updates a `LiveData`, and this has to occur on the main thread)
186+
- At this point, the View should be all set to start displaying suggestions according to the user's queries
187+
2. "Loading" state
188+
- Exclude blank queries with `filterNot`
189+
- In `pairQueryToStateAtStart`, copy current state, triggering the loading or refresh indicator and making the query field unclearable
190+
- Dispatch upstream processing to a computing thread
191+
- Post the new state
192+
- Dispatch upstream processing to the main thread
193+
- From now on, the View should display a progress bar to indicate that processing is being done
194+
3. Fetch suggestions
195+
- In `fetchPlaces`, call the `/places` web service to fetch suggestions according to the query
196+
- Dispatch upstream processing to an IO thread (IO is for any input/output operation like disk access or calling a web service)
197+
- The View is still displaying the progress bar
198+
4. UI model (re)construction
199+
- Map the response of the web service to an UI model with `response.toUiModel()`
200+
- Copy the current state, updating its `answer` property with the UI model
201+
- Dispatch upstream processing to a computing thread
202+
- Post the new state
203+
- Dispatch upstream processing to the main thread
204+
- And now the View is displaying the suggestions that have been fetched from the web service
205+
206+
The most satisfying aspect of this experiment is that it allows to separate the main concerns of the autosuggestion feature:
207+
208+
- Taking the user's input (queries)
209+
- Preparation of an initial or "loading" state
210+
- Fetching places from a web service
211+
- Converting those places to a data model that is better suited to the View (UI model)
212+
- Send the UI model to the View
213+
214+
And thanks to Kotlin `Flow`s, each step is dispatched to the proper thread (computing, processing or the main thread).
215+
216+
Please note that this autosuggestion use case is a simple one. `Flow`s would also make it easy to compose these steps with additional processing or other data sources, for example:
217+
218+
- Compute the distance between the user's position and each suggested place
219+
- Display weather previsions at each suggested place
220+
- Display a ⭐ indicator when a suggested place is already saved as a user's favorite on the device's database
143221

144222
#### Taking out effects
145223

0 commit comments

Comments
 (0)