Skip to content

Commit 6c77f3c

Browse files
Merge pull request #13
feat(viewmodel): add debug logging and prevent race conditions
2 parents 7b6535c + 49a792a commit 6c77f3c

File tree

4 files changed

+64
-7
lines changed

4 files changed

+64
-7
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.yogiveloper.yonewsai.core.util
2+
3+
import android.util.Log
4+
import kotlinx.coroutines.CoroutineScope
5+
import kotlinx.coroutines.flow.StateFlow
6+
import kotlinx.coroutines.launch
7+
8+
/**
9+
* Extension function to log all emissions from a StateFlow.
10+
* Useful for debugging UI state changes in ViewModels.
11+
*/
12+
fun <T> StateFlow<T>.logChanges(
13+
tag: String,
14+
scope: CoroutineScope
15+
) {
16+
scope.launch {
17+
this@logChanges.collect {
18+
Log.d(tag, "UI state updated: $it")
19+
}
20+
}
21+
}

app/src/main/java/com/yogiveloper/yonewsai/modules/home_news/data/repository/NewsRepositoryImpl.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import javax.inject.Singleton
1010

1111
@Singleton
1212
class NewsRepositoryImpl @Inject constructor(
13-
private val api: NewsApiService
13+
private val api: NewsApiService,
1414
) : NewsRepository {
1515

1616
/**
@@ -36,6 +36,7 @@ class NewsRepositoryImpl @Inject constructor(
3636
* articleCache.putAll(...) is then used to efficiently
3737
* add all the new articles to the cache in one go.
3838
* */
39+
Log.d("NewsRepositoryImpl", "Caching ${domainArticles.size} articles")
3940
articleCache.clear()
4041
articleCache.putAll(domainArticles.associateBy { it.id })
4142

app/src/main/java/com/yogiveloper/yonewsai/modules/home_news/presentation/detail/NewsDetailViewModel.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import androidx.lifecycle.SavedStateHandle
44
import androidx.lifecycle.ViewModel
55
import androidx.lifecycle.viewModelScope
66
import com.yogiveloper.yonewsai.core.util.Resource
7+
import com.yogiveloper.yonewsai.core.util.logChanges
78
import com.yogiveloper.yonewsai.modules.home_news.domain.model.Article
89
import com.yogiveloper.yonewsai.modules.home_news.domain.usecase.GetArticleByIdUseCase
910
import dagger.hilt.android.lifecycle.HiltViewModel
11+
import kotlinx.coroutines.Job
1012
import kotlinx.coroutines.flow.MutableStateFlow
1113
import kotlinx.coroutines.flow.StateFlow
1214
import kotlinx.coroutines.launch
@@ -26,14 +28,23 @@ class NewsDetailViewModel @Inject constructor(
2628
val uiState: StateFlow<NewsDetailUiState> = _uiState
2729

2830
init {
31+
/**
32+
* because currently a data (repo) is on simple cache
33+
* and ID its already on state
34+
* safe to load directly
35+
* */
2936
val articleID = savedStateHandle.get<Int>("articleID")
3037
if(articleID !== null){
3138
fetchArticleByID(articleID)
3239
}
40+
uiState.logChanges("NewsDetailViewModel", viewModelScope)
3341
}
3442

43+
// prevent race-condition ex: on swipe refresh or fast rotate
44+
private var fetchJob: Job? = null
3545
private fun fetchArticleByID(id: Int){
36-
viewModelScope.launch {
46+
fetchJob?.cancel() // cancel old job if any
47+
fetchJob = viewModelScope.launch {
3748
when(val r = getArticleByIdUseCase(id)){
3849
is Resource.Success -> {
3950
_uiState.value = _uiState.value.copy(article = r.data)

app/src/main/java/com/yogiveloper/yonewsai/modules/home_news/presentation/home/NewsHomeViewModel.kt

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package com.yogiveloper.yonewsai.modules.home_news.presentation.home
22

3-
import android.util.Log
43
import androidx.lifecycle.ViewModel
54
import androidx.lifecycle.viewModelScope
65
import com.yogiveloper.yonewsai.core.util.Resource
6+
import com.yogiveloper.yonewsai.core.util.logChanges
77
import com.yogiveloper.yonewsai.modules.home_news.domain.model.Article
88
import com.yogiveloper.yonewsai.modules.home_news.domain.usecase.GetTopHeadlinesUseCase
99
import dagger.hilt.android.lifecycle.HiltViewModel
10+
import kotlinx.coroutines.Job
1011
import kotlinx.coroutines.flow.MutableStateFlow
1112
import kotlinx.coroutines.flow.StateFlow
1213
import kotlinx.coroutines.launch
@@ -21,21 +22,44 @@ data class NewsHomeUiState(
2122

2223
@HiltViewModel
2324
class NewsHomeViewModel @Inject constructor(
24-
private val getTopHeadlines: GetTopHeadlinesUseCase
25+
private val getTopHeadlines: GetTopHeadlinesUseCase,
26+
// private val savedStateHandle: SavedStateHandle
2527
) : ViewModel() {
2628
private val _uiState = MutableStateFlow(NewsHomeUiState())
2729
val uiState: StateFlow<NewsHomeUiState> = _uiState
2830

2931
init {
30-
fetchBreakingNews()
32+
uiState.logChanges("NewsHomeViewModel", viewModelScope)
33+
/**
34+
* if API doesn't have cache mechanism should use these approach:
35+
* (else this expensive solution)
36+
* for HomeScreen should save on repo/local cache (Room/DataStore)
37+
* */
38+
39+
/**
40+
* if API doesn't have or have cache mechanism not problem use these approach:
41+
* for DetailScreen should save ID on state
42+
* then getArticleById from repo/local cache (Room/DataStore)
43+
* */
44+
45+
// val cached = savedStateHandle.get<List<Article>>("articles")
46+
// if (cached != null) {
47+
// _uiState.value = _uiState.value.copy(articles = cached)
48+
// } else {
49+
fetchBreakingNews()
50+
// }
51+
3152
}
3253

54+
// prevent race-condition ex: on swipe refresh or fast rotate
55+
private var fetchJob: Job? = null
3356
fun fetchBreakingNews(country: String = "us", category: String = "technology") {
34-
Log.d("fetchBreakingNews", "fetchBreakingNews: called")
35-
viewModelScope.launch {
57+
fetchJob?.cancel() // cancel old job if any
58+
fetchJob = viewModelScope.launch {
3659
_uiState.value = _uiState.value.copy(isLoading = true, error = null)
3760
when(val r = getTopHeadlines(country, category )){
3861
is Resource.Success -> {
62+
// savedStateHandle["articles"] = r.data
3963
_uiState.value = _uiState.value.copy(
4064
isLoading = false,
4165
articles = r.data,

0 commit comments

Comments
 (0)