1. 개요
    이 글은 https://youtu.be/LM6B03sCFxY?list=LL 강좌를 수업하면서 남기는 메모입니다.

강좌를 보면 RequestState 의 유용한 점에 대하여 알리고 있고, Request 을 State 로 처리하면서 compose 의 특징을 살려 처리해서 이용도가 높은 wrapper 으로 보입니다.

  1. 소스
    소스 Url은 https://gist.github.com/stevdza-san/cca20eff9f2c4c7d783ffd0a0061b352 입니다.
// https://gist.github.com/stevdza-san/cca20eff9f2c4c7d783ffd0a0061b352

import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.runtime.Composable

sealed class RequestState<out T> {
    data object Idle : RequestState<Nothing>()
    data object Loading : RequestState<Nothing>()
    data class Success<T>(val data: T) : RequestState<T>()
    data class Error(val message: String) : RequestState<Nothing>()

    fun isLoading() = this is Loading
    fun isSuccess() = this is Success
    fun isError() = this is Error

    /**
     * Returns data from a [Success].
     * @throws ClassCastException If the current state is not [Success]
     *  */
    fun getSuccessData() = (this as Success).data
    fun getSuccessDataOrNull(): T? {
        return try {
            (this as Success).data
        } catch (e: Exception) {
            null
        }
    }

    /**
     * Returns an error message from an [Error]
     * @throws ClassCastException If the current state is not [Error]
     *  */
    fun getErrorMessage() = (this as Error).message
    fun getErrorMessageOrNull(): String? {
        return try {
            (this as Error).message
        } catch (e: Exception) {
            null
        }
    }

    @Composable
    fun DisplayResult(
        onIdle: (@Composable () -> Unit)? = null,
        onLoading: @Composable () -> Unit,
        onSuccess: @Composable (T) -> Unit,
        onError: @Composable (String) -> Unit,
    ) {
        AnimatedContent(
            targetState = this,
            transitionSpec = {
                fadeIn(tween(durationMillis = 300)) togetherWith
                        fadeOut(tween(durationMillis = 300))
            },
            label = "Content Animation"
        ) { state ->
            when (state) {
                is Idle -> {
                    onIdle?.invoke()
                }

                is Loading -> {
                    onLoading()
                }

                is Success -> {
                    onSuccess(state.getSuccessData())
                }

                is Error -> {
                    onError(state.getErrorMessage())
                }
            }
        }
    }
}
  1. 설명
    이 Wrapper 은 api 같은 request 를 처리 할때 그 상태에 따라서 다른 내용을 display 가 되어야 하는 것을 일반화 시킨 wrapper 을 이용하여 처리하는 소스입니다.

4가지 상태를 가지며 1,2 상태는 Data를 가지지 않고 3,4 상태는 Data를 가집니다.
그리고 3가지 논리값을 가지는 검사함수가 포함됩니다.
그리고 2가지의 Data를 추출하는 함수가 있는데 2가지의 Data에 Null 이포함되는 형과 아닌형으로 총 4개의 Data 추출함수가 있습니다.

그리고 중요한것이 상태에 따라 적절한 Lamda 함수부를 호출하는 DisplayResult함수가 있고,
DisplayResult함수에서 첫번째인 Idle 상태는 선택사항입니다. 즉 idle 상태일때는 아무런 처리가 없어도 괜찮습니다.

생성되는 예입니다.

  fun fetchDataWithWrapper(): Flow<RequestState<List<String>>>{
    return flow {
      emit(RequestState.Loading)
      delay(2000)
      emit(RequestState.Success(listOf("Testing","is","every thing")))
    }
  }
  1. repository 에서 생성예
fun fetchDataWithWrapper(): Flow<RequestState<List<String>>>{
    return flow {
      emit(RequestState.Loading)
      delay(2000)
      emit(RequestState.Success(listOf("Testing","is","every thing")))
    }
  }
  1. Viewmodel 에서 적용
val data = repository.fetchDataWithWrapper()
  1. UI 에서 적용예

val data = vm.data.collectAsState(initial = RequestState.Idle) // 초기값 선언

data.DisplayResult(
            onLoading = { MainContent() },
            onSuccess = { MainContent(text = data.getSuccessData().joinToString()) },
            onError = { MainContent(text = data.getErrorMessage()) }
          )

// 표시예

fun MainContent(
  modifier: Modifier = Modifier,
  text: String? = null
) {
  Box(modifier = Modifier.fillMaxSize()) {
    text?.let { Text(it) }
  }//box
}//mainContent

0개의 댓글