-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ISSUE-011] BaseViewModel 및 샘플 데이터 생성 #27
Changes from all commits
9039bd3
e16fc3b
d5e4061
66079bf
301a42c
b24f39b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.yapp.growth.base | ||
|
||
import androidx.lifecycle.ViewModel | ||
import androidx.lifecycle.viewModelScope | ||
import kotlinx.coroutines.channels.Channel | ||
import kotlinx.coroutines.flow.* | ||
import kotlinx.coroutines.launch | ||
|
||
abstract class BaseViewModel<S : ViewState, A : ViewSideEffect, E : ViewEvent>( | ||
initialState: S | ||
) : ViewModel() { | ||
|
||
abstract fun handleEvents(event: E) | ||
|
||
private val _viewState: MutableStateFlow<S> = MutableStateFlow<S>(initialState) | ||
val viewState = _viewState.asStateFlow() | ||
|
||
private val currentState: S | ||
get() = _viewState.value | ||
|
||
private val _effect: Channel<A> = Channel() | ||
val effect = _effect.receiveAsFlow() | ||
|
||
private val _event: MutableSharedFlow<E> = MutableSharedFlow() | ||
|
||
protected fun updateState(reducer: S.() -> S) { | ||
val newState = currentState.reducer() | ||
_viewState.value = newState | ||
} | ||
|
||
protected fun sendEffect(vararg builder: () -> A) { | ||
for (effectValue in builder) { | ||
viewModelScope.launch { _effect.send(effectValue()) } | ||
} | ||
} | ||
|
||
open fun setEvent(event : E) { | ||
deliverEvent(event) | ||
} | ||
|
||
private fun deliverEvent(event : E) = viewModelScope.launch { | ||
handleEvents(event) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package com.yapp.growth.base | ||
|
||
interface ViewEvent { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package com.yapp.growth.base | ||
|
||
interface ViewSideEffect { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package com.yapp.growth.base | ||
|
||
interface ViewState { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.yapp.growth.ui.sample | ||
|
||
import com.yapp.growth.base.ViewEvent | ||
import com.yapp.growth.base.ViewSideEffect | ||
import com.yapp.growth.base.ViewState | ||
|
||
class SampleContract { | ||
|
||
data class SampleViewState( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저희 View, State, Event, Effect의 네이밍을 어떻게 가져갈 것인지 정해야 할 것 같은데 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 베이스 짜면서 이렇게 생각했어요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아하 올려주신 Sample 보고 옮겨 적은 거였는데,, 제가 잘못 옮겨적었네요.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋아요! 그럼 다른건 그대로 유지하되 |
||
val isLoading: Boolean = false | ||
) : ViewState | ||
|
||
sealed class SampleSideEffect : ViewSideEffect { | ||
object NavigateToAnyScreen : SampleSideEffect() | ||
data class ShowToast(val msg: String) : SampleSideEffect() | ||
} | ||
|
||
sealed class SampleEvent : ViewEvent { | ||
object OnAnyButtonClicked : SampleEvent() | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.yapp.growth.ui.sample | ||
|
||
import com.yapp.growth.base.BaseViewModel | ||
import com.yapp.growth.domain.usecase.UseCase | ||
import com.yapp.growth.ui.sample.SampleContract.* | ||
|
||
class SampleViewModel( | ||
private val useCase: UseCase | ||
) : BaseViewModel<SampleViewState, SampleSideEffect, SampleEvent>( | ||
SampleViewState() | ||
) { | ||
|
||
private fun anyFunction() { | ||
// anything do . . . | ||
sendEffect( | ||
{ SampleSideEffect.NavigateToAnyScreen }, | ||
{ SampleSideEffect.ShowToast("This is Sample Data") } | ||
) | ||
updateState { copy(isLoading = false) } | ||
} | ||
|
||
override fun handleEvents(event: SampleEvent) { | ||
when (event) { | ||
is SampleEvent.OnAnyButtonClicked -> { | ||
updateState { copy(isLoading = true) } | ||
anyFunction() | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SharedFlow 대신 Channel을 사용한 장점이 있나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
effect 의 경우 어떤 이벤트가 발생했을 때 한번만 실행되어야 하는데, 모든 구독자에게 데이터를 방출하는
SharedFlow
를 사용하는 것은 옳지 않다고 생각합니다. 반면Channel
의 경우 한 구독자에게 한번씩만 데이터를 방출하기 때문에, 사용하기 적절하다고 판단했습니다.SharedFlow
의 경우 구독자가 나타나지 않아도 데이터가 방출되는 반면,Channel
의 경우 구독자가 없을 경우 방출되지 않는데 이 요소 또한 중요하다고 생각합니다.이 두 이유를 근거로 제가 생각한 하나의 예시를 들어보겠습니다.
라고 판단했는데, 부족한 점이나 틀린 점 지적해주시면 감사하겠습니다 🤣