Blog Infos
Author
Published
Topics
, , , ,
Author
Published
Photo by Natasya Chen on Unsplash

In the domain of Android development, the pursuit of efficiency and robustness requires leveraging advanced language features and architectural best practices. Kotlin’s sealed interfaces emerge as a critical tool in this context, especially for managing diverse string resources effectively. This article delves into the strategic application of sealed interfaces within Android applications, underscored by insights from Android’s testing fundamentals and ViewModel design patterns. 📚

https://mrnajafi.medium.com/can-you-create-a-bookmark-button-in-jetpack-compose-c412b3f88824?source=post_page—–0882a3d2afd1——————————–
What are Sealed Interfaces?

Sealed interfaces in Kotlin act as a sophisticated mechanism for defining restricted hierarchies, allowing developers to specify a finite set of types for particular scenarios. This capability ensures exhaustive handling of cases in when expressions, thus enhancing type safety and the predictability of the application’s behavior.

Learn more about this feature directly from the source at Kotlin’s official documentation on sealed classes 🪄📜.

A Story About Text in Your App 📚🎉

ViewModels in Android architecture act as a bridge between the app’s data layer and its UI, tasked with handling business logic while staying decoupled from the Android framework. According to Android’s testing fundamentals, and insights on ViewModel patterns (Locale changes and the AndroidViewModel anti-pattern), it’s recommended to avoid direct use of Android’s resources within ViewModels. This is where the StringResource sealed interface comes into play.

The StringResource sealed interface provides a strategic solution for abstracting the handling of string resources, facilitating a clean separation of concerns. This abstraction not only adheres to the principles of clean architecture but also ensures that ViewModels can remain agnostic of the Android context, thus enhancing testability and maintainability.

@Stable
sealed interface StringResource {
fun resolve(context: Context): String
data class Text(val text: String): StringResource {
override fun resolve(context: Context): String {
return text
}
}
data class ResId(@StringRes val stringId: Int): StringResource {
override fun resolve(context: Context): String {
return context.getString(stringId)
}
}
data class ResIdWithParams(@StringRes val stringId: Int, val params: List<Any>): StringResource {
override fun resolve(context: Context): String {
return context.getString(stringId, *params.toTypedArray())
}
}
@Composable
fun resolve(): String {
return when (this) {
is ResId -> stringResource(id = stringId)
is ResIdWithParams -> stringResource(id = stringId, *params.toTypedArray())
is Text -> text
}
}
}

This approach allows developers to encapsulate the logic for resolving string resources, enabling ViewModels to interact with string representations without directly depending on Android’s context. It exemplifies a model of development where business logic and UI concerns are cleanly separated, aligning with best practices for scalable and maintainable codebases.

We can use the class above in error formatters which could be used inside ViewModels:

class PricesViewModel @Inject constructor(
errorFormatter: dagger.Lazy<ErrorFormatter>,
pricesUseCase: dagger.Lazy<GetPricesUseCase>
): ViewModel() {
val state = pricesUseCase.get().invoke()
.map {
State.Success(it)
}
.catch {
val error = errorFormatter.get().format(it)
emit(State.Error(error))
}.stateInDefault(viewModelScope, State.Loading)
}
interface ErrorFormatter {
fun format(throwable: Throwable?) : StringResource
}
class ErrorFormatterDefault: ErrorFormatter {
override fun format(throwable: Throwable?) : StringResource {
return when (throwable) {
is SocketTimeoutException-> StringResource.ResId(R.string.not_connected_to_internet)
else -> StringResource.Text("Something went wrong!")
}
}
}

And then state’s error case can be easily handled inside the UI layer without dealing with error handling or parsing the exception.

if (state is State.Error) {
ErrorView(state.resolve())
}

OUR VIDEO RECOMMENDATION

, ,

Migrating to Jetpack Compose – an interop love story

Most of you are familiar with Jetpack Compose and its benefits. If you’re able to start anew and create a Compose-only app, you’re on the right track. But this talk might not be for you…
Watch Video

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engineer for Jetpack Compose
Google

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engin ...
Google

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engineer f ...
Google

Jobs

No results found.

Advantages of This Approach
  • Type Safety and Predictability: Sealed interfaces ensure that all possible types are considered, reducing the likelihood of runtime errors and increasing the robustness of the application.
  • Architectural Cleanliness: By abstracting string resources through a sealed interface, the architecture remains clean, with a clear separation between the app’s logic and its UI, facilitating easier testing and maintenance.
  • ViewModel Integrity: This methodology allows ViewModels to remain pure in their function, devoid of direct dependencies on the Android framework, thus adhering to the principles of clean architecture.
Conclusion

The strategic application of Kotlin’s sealed interfaces in Android development, particularly for managing string resources, represents a sophisticated approach to achieving type safety, architectural cleanliness, and maintainability. By abstracting string resources and adhering to best practices in ViewModel design, developers can build scalable and robust applications. This professional methodology underscores the importance of leveraging advanced language features and architectural patterns to enhance the quality and efficiency of Android applications. As the Android ecosystem continues to evolve, embracing such practices will be pivotal in navigating the complexities of app development successfully.

Summary
  • Avoid direct framework dependencies in classes containing business logic. For example, don’t use Android Contexts in ViewModels.
  • Avoid dealing with objects that have a lifecycle in ViewModels.
  • Try not to include any Android imports as much as possible in ViewModels (if you’re using livedata or paging directly in viewmodel would be OK to keep or go Flow!)
  • Instead of passing resources specially strings around use the StringResource or a similar implementation

We’ve journeyed through the strategic landscapes of Kotlin’s sealed interfaces, unlocking the secrets to more robust, maintainable, and efficient Android development. By embracing these advanced features and best practices, we’re not just coding; we’re crafting future-proof applications that stand the test of time and technology shifts.

Please share 👏 if you’ve liked the article. Happy coding! 👨‍💻 👩‍💻

Connect with me on LinkedIn and Twitter for collaboration.

You might be interested in the following article as well:
https://proandroiddev.com/debug-like-a-boss-cracking-the-code-of-android-api-calls-f804be039c5a?source=post_page—–0882a3d2afd1——————————–

https://levelup.gitconnected.com/think-you-know-if-else-statements-think-again-d0a1ef336e4f?source=post_page—–0882a3d2afd1——————————–

https://proandroiddev.com/goodbye-gson-hello-moshi-4e591116231e?source=post_page—–0882a3d2afd1——————————–

This vblog is previously published on proandroiddev.com

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
Hi, today I come to you with a quick tip on how to update…
READ MORE
blog
Automation is a key point of Software Testing once it make possible to reproduce…
READ MORE
blog
Drag and Drop reordering in Recyclerview can be achieved with ItemTouchHelper (checkout implementation reference).…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu