Core Module
The foundation layer with zero UI dependencies. Every other module in Miru SDK depends on :core.
implementation("com.github.wahidabd.miru-sdk:core:<version>")
Resource
Resource<T> is a sealed class that wraps all async operations into three states:
sealed class Resource<T> {
class Success<T>(val data: T) : Resource<T>()
class Error<T>(val exception: AppException, val data: T? = null) : Resource<T>()
class Loading<T>(val data: T? = null) : Resource<T>()
}
Callbacks
val result: Resource<User> = userApi.getUserById(1)
result
.onSuccess { user -> println(user.name) }
.onError { exception, data -> println(exception.message) }
.onLoading { println("Loading...") }
Transformations
// Transform the success data
val mapped: Resource<String> = result.map { it.name }
// Chain operations
val chained: Resource<Profile> = result.flatMap { user ->
profileApi.getProfile(user.id)
}
Data Retrieval
val user: User? = result.getOrNull()
val userSafe: User = result.getOrDefault(User.empty())
val userForced: User = result.getOrThrow()
Flow Conversion
// Convert Flow<T> to Flow<Resource<T>>
val resourceFlow: Flow<Resource<List<User>>> = userFlow.asResource()
info
See Resource API Reference for the complete API.
AppException
Typed exception hierarchy for structured error handling across layers:
sealed class AppException(message: String?) : Exception(message) {
class NetworkException(message: String?) : AppException(message)
class TimeoutException(message: String?) : AppException(message)
class UnauthorizedException(message: String?) : AppException(message)
class ForbiddenException(message: String?) : AppException(message)
class NotFoundException(message: String?) : AppException(message)
class ServerException(val code: Int, message: String?) : AppException(message)
class UnknownException(cause: Throwable?) : AppException(cause?.message)
}
Pattern match in your ViewModel or global error handler:
when (exception) {
is AppException.UnauthorizedException -> navigateToLogin()
is AppException.NetworkException -> showOfflineMessage()
is AppException.ServerException -> showServerError(exception.code)
is AppException.TimeoutException -> showRetryDialog()
else -> showGenericError()
}
tip
See Error Handling Guide for best practices.
Mapper
Type-safe mapping interface for DTO-to-domain conversions:
class UserMapper : Mapper<UserDto, User> {
override fun map(from: UserDto) = User(
id = from.id,
name = from.name,
email = from.email
)
}
Extensions
String Extensions
"hello world".capitalizeFirst() // "Hello world"
"test@email.com".isValidEmail() // true
Flow Extensions
flow.throttleFirst(300L)
flow.retryWithExponentialBackoff(maxRetries = 3)
flow.asResource() // Flow<T> → Flow<Resource<T>>
Collection Extensions
list.safeGet(99) // null instead of IndexOutOfBoundsException
list.updateIf({ it.id == 5 }) { it.copy(name = "Updated") }
DateTime Extensions
Kotlinx DateTime helpers for common date/time operations.
Logger
Napier-based multiplatform logging:
MiruLogger.d("Debug message")
MiruLogger.e("Error message", throwable)