
Represents session management, allowing multiple starts and ends while holding and atomically updating data. Notifies observers on data changes and supports synchronous or streaming data retrieval.
Session class represents a concept of a session:
data flow),⚠ Be aware that
dataflow is conflated. If it is important that no item is missed the flow should be subscribed usingDispatchers.Unconfineddispatcher.
It is also a good idea to start the subscription usingCoroutineStart.UNDISPATCHEDstart method - otherwise the subscription may not be synchronous.
internal access modifier should be enough in most cases,singleOf in Koin)Start_XXX_SessionUseCase
internal class LoginSession : Session<LoginSessionData>()
internal data class LoginSessionData(
val username: String,
val password: String
)val domainModule = module {
...
singleOf(::LoginSession)
...
}ℹ You must always provide initial data while starting session.
class BeginLoginProcessUseCase internal constructor(
private val loginSession: LoginSession
) {
operator fun invoke() {
if (!loginSession.isStarted) {
loginSession.start(LoginSessionData("", ""))
}
}
}class UpdateCollectedUsernameUseCase internal constructor(
private val loginSession: LoginSession
) {
operator fun invoke(username: String) {
loginSession.update { sessionData ->
sessionData.copy(username = username)
}
}
}class GetAccessTokenUseCase internal constructor(
private val loggedInSession: LoggedInSession
) {
operator fun invoke(): String = loggedInSession.requireData().accessToken
}class GetCollectedUsernameUseCase internal constructor(
private val loginSession: LoginSession
) {
operator fun invoke(): Flow<String> = loginSession.data.map { data -> data!!.username }
}class IsCollectedUsernameFormatValidUseCase internal constructor(
private val loginSession: LoginSession
) {
operator fun invoke(): Flow<UsernameValidationResult> = loginSession
.data
.map { sessionData ->
sessionData?.username?.let { username ->
val length = username.length
when {
length == 0 -> NOT_AVAILABLE
length < 6 -> TOO_SHORT
length > 12 -> TOO_LONG
else -> VALID
}
} ?: NOT_AVAILABLE
}
}
enum class UsernameValidationResult {
NOT_AVAILABLE,
TOO_SHORT,
TOO_LONG,
VALID
}class AbortLoginProcessUseCase internal constructor(
private val loginSession: LoginSession
) {
operator fun invoke() {
if (loginSession.isStarted) {
loginSession.end()
}
}
}Session class represents a concept of a session:
data flow),⚠ Be aware that
dataflow is conflated. If it is important that no item is missed the flow should be subscribed usingDispatchers.Unconfineddispatcher.
It is also a good idea to start the subscription usingCoroutineStart.UNDISPATCHEDstart method - otherwise the subscription may not be synchronous.
internal access modifier should be enough in most cases,singleOf in Koin)Start_XXX_SessionUseCase
internal class LoginSession : Session<LoginSessionData>()
internal data class LoginSessionData(
val username: String,
val password: String
)val domainModule = module {
...
singleOf(::LoginSession)
...
}ℹ You must always provide initial data while starting session.
class BeginLoginProcessUseCase internal constructor(
private val loginSession: LoginSession
) {
operator fun invoke() {
if (!loginSession.isStarted) {
loginSession.start(LoginSessionData("", ""))
}
}
}class UpdateCollectedUsernameUseCase internal constructor(
private val loginSession: LoginSession
) {
operator fun invoke(username: String) {
loginSession.update { sessionData ->
sessionData.copy(username = username)
}
}
}class GetAccessTokenUseCase internal constructor(
private val loggedInSession: LoggedInSession
) {
operator fun invoke(): String = loggedInSession.requireData().accessToken
}class GetCollectedUsernameUseCase internal constructor(
private val loginSession: LoginSession
) {
operator fun invoke(): Flow<String> = loginSession.data.map { data -> data!!.username }
}class IsCollectedUsernameFormatValidUseCase internal constructor(
private val loginSession: LoginSession
) {
operator fun invoke(): Flow<UsernameValidationResult> = loginSession
.data
.map { sessionData ->
sessionData?.username?.let { username ->
val length = username.length
when {
length == 0 -> NOT_AVAILABLE
length < 6 -> TOO_SHORT
length > 12 -> TOO_LONG
else -> VALID
}
} ?: NOT_AVAILABLE
}
}
enum class UsernameValidationResult {
NOT_AVAILABLE,
TOO_SHORT,
TOO_LONG,
VALID
}class AbortLoginProcessUseCase internal constructor(
private val loginSession: LoginSession
) {
operator fun invoke() {
if (loginSession.isStarted) {
loginSession.end()
}
}
}