```@HiltViewModel class HomeViewModel @Inject cons...
# android-architecture
m
Copy code
@HiltViewModel
class HomeViewModel @Inject constructor(
    repository: Repository
) : ViewModel() {

    val uiState = repository.data
        .mapLatest { result ->
            when (result) {
                is Result.Success -> UiState.Success(result)
                is Result.Error -> UiState.Error(result.exception.message ?: "Unknown error")
            }
        }.stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5.seconds.inWholeMilliseconds),
            initialValue = UiState.Loading
        )
    
    sealed class UiState {
        data object Loading : UiState()
        data class Success(val data: Data) : UiState()
        data class Error(val message: String) : UiState()
    }
}

class Repository @Inject constructor(
    private val api: Api
) {
    private val _data = MutableStateFlow<Result<Response>?>(null)
    val data = _data.asStateFlow().filterNotNull()

    suspend fun getData() {
        try {
            val response = api.getDataFromNetwork()
            if (response.isSuccessful) {
                val body = response.body()
                if (body != null) {
                    _data.emit(Result.Success(body))
                }
            } else {
                val exception = Exception("Server Error: ${response.code()}")
                _data.emit(Result.Error(exception))
            }
        } catch (e: Exception) {
            _data.emit(Result.Error(e))
        }
    }
}
The above is the approach suggest in the architecture samples HERE but I don't understand how it'll work for network call where caching or offline support is not required. The two main problems I am facing with this approach for the above are: 1. How should I trigger the initial data load when the screen opens? 2. What's the best way to implement a refresh mechanism (like pull-to-refresh)? Is this Flow-based approach overkill for simple network calls without caching? Should I consider a different approach? Would appreciate any guidance or best practices!
a
> but I don't understand how it'll work for network call where caching or offline support is not required There are few errors in the approach replication. Think of data/network call as cold flow. Cold flow starts when a subscriber appears. The next thing is that you may want to map the response to what you want to show in the UI. Thus
mapLatest
. Let's start by rewriting the code to adjust and solidify our understanding. We do not want any kind of state variable in the repository, so we eliminate private val _data = MutableStateFlow<ResultResponse?>(null) val data = _data.asStateFlow().filterNotNull() Now we are left with
getData()
function. We change it to cold flow by using the
flow
builder (cold).
Copy code
fun getData() = flow {
    …
}
This will return us a cold flow. Now within
ViewModel
we will add relevant operators.
Copy code
val uiState = repository.getData()
    .mapLatest {
         // Transform data here to ui state
     }.stateIn (
          …
       )
> How should I trigger the initial data load when the screen opens? We finally used
stateIn
to convert cold flow to hot flow. Now, when a subscribe appears on uiState, the data will start 'flowing'. > What's the best way to implement a refresh mechanism Use a trigger mechanism. If using compose, you can use
snapshotflow
Also, next time when there is a long message, consider prefacing the question and then adding 🧵 . You can then reply yourself with all the relevant details.
m
thanks for the reply.
We do not want any kind of state variable in the repository, so we eliminate
Do you mean we don't want the state variables only in this case? but is acceptable in other cases as in the sample.
Use a trigger mechanism. If using compose, you can use
snapshotflow
Also, can you please provide a sample for this if possible like above? I'm using compose
a
Please read the documentation.
Classes in the data layer generally expose functions to perform one-shot Create, Read, Update and Delete (CRUD) calls or to be notified of data changes over time.
It will answer a lot of your questions.
👍 2
You can read about snapshotflow here. A working example that demonstrates the usage is here and the github link of that example is here.
m
Thank you, that was helpful. One final question I had was if we change the
getData()
function to be a cold flow and observe it in ViewModel using
stateIn
do we not need to specify coroutine dispatcher for it? like
withContext(<http://Dispatchers.IO|Dispatchers.IO>)
as a network call should be main safe?
a
You didn’t read the Data Layers docs completely .. :))
There is a topic with
Threading
.
Note that most data sources already provide main-safe APIs like the suspend method calls provided by Room, Retrofit or Ktor. Your repository can take advantage of these APIs when they are available.
So it depends on the client you are using. But you can still use
Copy code
.flowOn(ioDispatcher)
for your flow.
🙌 1