<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Peter Chege</title>
    <description>The latest articles on DEV Community by Peter Chege (@peter_chege79).</description>
    <link>https://dev.to/peter_chege79</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F780251%2F1a7c1df4-ab81-4516-b938-7fe3e9d6ab42.jpg</url>
      <title>DEV Community: Peter Chege</title>
      <link>https://dev.to/peter_chege79</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/peter_chege79"/>
    <language>en</language>
    <item>
      <title>Effortlessly Managing API Keys and Secrets in your android app using the secrets Gradle Plugin</title>
      <dc:creator>Peter Chege</dc:creator>
      <pubDate>Mon, 27 Nov 2023 14:08:56 +0000</pubDate>
      <link>https://dev.to/peter_chege79/effortlessly-managing-api-keys-and-secrets-in-your-android-app-using-the-secrets-gradle-plugin-3hbl</link>
      <guid>https://dev.to/peter_chege79/effortlessly-managing-api-keys-and-secrets-in-your-android-app-using-the-secrets-gradle-plugin-3hbl</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Hello guys 👋👋, welcome back to another article, in our previous article we discussed&lt;br&gt;
&lt;a href="https://medium.com/stackademic/this-trick-will-save-you-time-when-dealing-with-the-problems-of-navigation-compose-in-jetpack-4a63ea505c0a"&gt;how to navigate the obstacles of Navigation Compose&lt;/a&gt;.&lt;br&gt;
In this article, we'll learn how to effortlessly manage our API Keys and secrets in our android project using &lt;br&gt;
the secrets Gradle Plugin&lt;/p&gt;

&lt;p&gt;Normally when working in our android projects we find ourselves consuming different kinds of APIs which &lt;br&gt;
probably need API Keys to be accessed. Normally we'd want to exclude these API Keys from our version control&lt;br&gt;
for security purpose, so what we do is include the given API Key in our local.properties file generated&lt;br&gt;
by Android Studio which is by default excluded from version control. However, reading from this file in &lt;br&gt;
our project sometimes involves some boilerplate code in our build scripts. Some of the boilerplate would &lt;br&gt;
look something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Read API_KEY for build file 
val API_KEY: String = gradleLocalProperties(rootDir).getProperty("API_KEY")

// Include it as a build field in your build variants
...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So to deal with this issue the Google team came up with a plugin called the &lt;a href="https://github.com/google/secrets-gradle-plugin"&gt;Secrets Gradle Plugin&lt;/a&gt; &lt;br&gt;
that abstracts that build logic for us and does everything for us behind the scenes. The need for this plugin &lt;br&gt;
arose when they noticed developers need to hide their Maps API Keys when working with Google Maps in their &lt;br&gt;
android projects &lt;/p&gt;

&lt;p&gt;Using the secrets plugins is simple... Just add the plugin to your project and apply it to your app module then &lt;br&gt;
it will read from your local.properties file. The read variables will be included in your generated BuildConfig class at&lt;br&gt;
compile time. For more information on customization check our their &lt;a href="https://github.com/google/secrets-gradle-plugin"&gt;README.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading and I hope you learnt something new until next time .....Bye.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Navigating the obstacles of Navigation Compose</title>
      <dc:creator>Peter Chege</dc:creator>
      <pubDate>Fri, 08 Sep 2023 16:41:46 +0000</pubDate>
      <link>https://dev.to/peter_chege79/navigating-the-obstacles-of-navigation-compose-39gm</link>
      <guid>https://dev.to/peter_chege79/navigating-the-obstacles-of-navigation-compose-39gm</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Hello guys 👋👋, welcome back to another article, in our previous article&lt;br&gt;
we&lt;br&gt;
discussed &lt;a href="https://medium.com/@peterkagure/debug-and-inspect-your-network-requests-in-android-using-the-chucker-dev-extension-with-retrofit-or-2d7b82c51012"&gt;how to inspect network traffic using the Chucker dev extension in android&lt;/a&gt;.&lt;br&gt;
In this article, I will show you a trick that can save you time when dealing with the nuisances of Navigation Compose&lt;/p&gt;

&lt;p&gt;If you've used navigation compose, you may have noticed how sometimes it can&lt;br&gt;
be a headache because it lacks compile time type safety, hence sometimes when&lt;br&gt;
you navigate to a given route, your app crashes.This problem has led to the creation of &lt;br&gt;
type safe navigation libraries like compose destinations, voyager etc.&lt;br&gt;
This can reduce your productivity as may spend time to debug the crash. &lt;br&gt;
Basically, to prevent the reoccurrence such crashes, we should abstract our &lt;br&gt;
navigation definitions through state hoisting. Don't understand what this &lt;br&gt;
means ....read on&lt;/p&gt;

&lt;p&gt;Basically, state hoisting is practise in Jetpack Compose where we only expose&lt;br&gt;
state and functions that modify the state to the composable.This results to&lt;br&gt;
having 2 versions of one composable, Stateless and a stateful one.For more&lt;br&gt;
information and the benefits of this practise,&lt;br&gt;
read &lt;a href="https://medium.com/@peterkagure/stateful-and-stateless-components-in-jetpack-compose-18f89f790750"&gt;this article&lt;/a&gt;&lt;br&gt;
by me&lt;/p&gt;
&lt;h2&gt;
  
  
  How we navigate In Jetpack Compose
&lt;/h2&gt;

&lt;p&gt;Normally we navigate in compose by passing the navigation logic to the&lt;br&gt;
onClick lambda where its needed as shown below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Button(
    modifier = Modifier.fillMaxWidth(),
    onClick = {
        navController.navigate(route="login")
    }
) {
      Text(text = "Login")

    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem with this approach is that when if the NavHost where this navController&lt;br&gt;
is defined does not have a route called "login", the app will crash. Normally if&lt;br&gt;
you have one NavHost this won't be the issue since there is only one instance&lt;br&gt;
of the navController(Unless you create a new one in your composable), but when&lt;br&gt;
your app has a bottom Nav bar that requires its own NavHost, this approach can&lt;br&gt;
become an issue since now you have multiple navControllers&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;To solve this issue, we can define all necessary navigation definitions in &lt;br&gt;
our NavHost via explicit lambdas or extension functions to the NavController&lt;br&gt;
class. This way we know what NavController has a route definition as shown below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val navController = rememberNavController()

NavHost(
    navController = navController,
    startDestination = Screens.DASHBOARD_SCREEN
    ) {
        composable(route = Screens.LOGIN_SCREEN) {
            LoginScreen(
                navigateToDashBoard = navController::navigateToDashBoard,
                navigateToSignUpScreen = navController::navigateToSignUpScreen,
            )

        }
        composable(route = Screens.SIGNUP_SCREEN) {
            SignUpScreen(
                navigateToLoginScreen = navController::navigateToLoginScreen,
            )
        }
        composable(route = Screens.DASHBOARD_SCREEN) {
            DashboardScreen(
                navigateToLoginScreen = navController::navigateToLoginScreen,
            )
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way we can pass the navigation functions to the composables without explictly &lt;br&gt;
having to define the navigation logic each time we need it. If we get the navigation&lt;br&gt;
logic right top level, we won't need to worry about getting it right when actually &lt;br&gt;
navigating to the various screens.&lt;/p&gt;

&lt;p&gt;Once again this won't help much if you have 1 NavHost you will see its benefits if&lt;br&gt;
you have a bottom App Bar in your application &lt;/p&gt;

&lt;p&gt;I hope this tricks helps you build better apps using Jetpack compose.&lt;br&gt;
Don't forget to clap for this article and leave a comment for any questions&lt;/p&gt;

&lt;p&gt;While you are at it, you can follow my &lt;a href="https://github.com/chege4179"&gt;GitHub&lt;/a&gt; &lt;br&gt;
profile to see some projects this in concept in practice&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Debug and inspect your network requests in android using the Chucker dev extension with Retrofit or Ktor Client</title>
      <dc:creator>Peter Chege</dc:creator>
      <pubDate>Fri, 08 Sep 2023 16:40:33 +0000</pubDate>
      <link>https://dev.to/peter_chege79/debug-and-inspect-your-network-requests-in-android-using-the-chucker-dev-extension-with-retrofit-or-ktor-client-1bl8</link>
      <guid>https://dev.to/peter_chege79/debug-and-inspect-your-network-requests-in-android-using-the-chucker-dev-extension-with-retrofit-or-ktor-client-1bl8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Hello guys 👋👋, welcome back to another article, in our previous article we discussed&lt;br&gt;
&lt;a href="https://medium.com/@peterkagure/how-to-migrate-your-database-in-android-with-room-while-also-testing-your-migrations-bd90d2163d10"&gt;how to run migrations in android while also testing them &lt;/a&gt;.&lt;br&gt;
In this article, we'll learn how to inspect and debug our network requests in android using the chucker dev extension&lt;/p&gt;

&lt;p&gt;As an android developer, we normally usually interact with backend servers via REST APIs.Normally we use&lt;br&gt;
http client libraries like Retrofit and Ktor client. The chucker dev extension gives us an interface that&lt;br&gt;
can help us see all the information about all the requests and responses like the JSON payload and response status codes&lt;br&gt;
etc. In this article we'll see how we can add the extension when using either Retrofit or Ktor client&lt;/p&gt;
&lt;h2&gt;
  
  
  Add the necessary dependencies
&lt;/h2&gt;

&lt;p&gt;The chucker dev extension is available on &lt;a href="https://github.com/ChuckerTeam/chucker"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To start we'll add the necessary dependencies in our build.gradle.kts (App Module) as shown below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    dependencies {
        ... other dependencies
        debugImplementation ("com.github.chuckerteam.chucker:library:3.5.2")
        releaseImplementation ("com.github.chuckerteam.chucker:library-no-op:3.5.2")
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The library has 2 variants namely a debug and release implementation. The release implementation is used to disable the&lt;br&gt;
interface window&lt;br&gt;
in production builds of your app so your users can not see the http activity associated with the given app.&lt;/p&gt;

&lt;p&gt;After that, we'll proceed in 2 ways depending on the http client you are using&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Retrofit
&lt;/h3&gt;

&lt;p&gt;When using retrofit, we'll add the following interceptor to our Retrofit instance like shown below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;
    &lt;span class="nd"&gt;@Provides&lt;/span&gt;
    &lt;span class="nd"&gt;@Singleton&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;provideHttpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@ApplicationContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;OkHttpClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;OkHttpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addInterceptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;ChuckerInterceptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ChuckerCollector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;maxContentLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;250000L&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redactHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headerNames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;emptySet&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alwaysReadResponseBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Provides&lt;/span&gt;
    &lt;span class="nd"&gt;@Singleton&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;provideUserApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OkHttpClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;networkJson&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;BloggerApi&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Retrofit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Constants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addConverterFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;networkJson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asConverterFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toMediaType&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BloggerApi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, we are creating an okhttp client that adds the chucker extension as an interceptor. We then provide&lt;br&gt;
that client to the Retrofit instance. In the example above, I am using Dagger hilt&lt;/p&gt;

&lt;p&gt;That's really it for installing it using Retrofit.&lt;br&gt;
When you run your app, you should see a notification on your device that will log all different network requests that the &lt;br&gt;
app makes as the user navigates through the app.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Ktor Client
&lt;/h3&gt;

&lt;p&gt;To set up chucker using the ktor client,we'll first create http client factory like shown below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class HttpClientFactory {

    fun create(engine: HttpClientEngine) = HttpClient(engine) {

        install(ContentNegotiation) {
            json(
                Json {
                    prettyPrint = true
                    isLenient = true
                    ignoreUnknownKeys = true
                }
            )
        }

        install(DefaultRequest) {
            header(HttpHeaders.ContentType, ContentType.Application.Json)
            header("x-api-key",Constants.API_KEY)

        }

        expectSuccess = true
        addDefaultResponseValidation()
        if (BuildConfig.DEBUG) {
            install(Logging) {
                logger = Logger.DEFAULT
                level = LogLevel.ALL
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we'll then add the following in our app's network Module&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    @Provides
    @Singleton
    fun provideChuckerInterceptor(@ApplicationContext context: Context): ChuckerInterceptor {
        return ChuckerInterceptor.Builder(context)
            .collector(ChuckerCollector(context))
            .maxContentLength(250000L)
            .redactHeaders(emptySet())
            .alwaysReadResponseBody(false)
            .build()
    }

    @Provides
    @Singleton
    fun provideHttpClientEngine(chuckerInterceptor: ChuckerInterceptor): HttpClientEngine = OkHttp.create {
        addInterceptor(chuckerInterceptor)
    }


    @Provides
    @Singleton
    fun provideHttpClient(engine: HttpClientEngine): HttpClient = HttpClientFactory().create(engine)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that you're set to go.&lt;br&gt;
When you run your app, you should see a notification recording all your http activity &lt;/p&gt;

&lt;p&gt;I hope this helped you and made debugging your network requests easier&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to migrate your database in android with Room while also testing your migrations</title>
      <dc:creator>Peter Chege</dc:creator>
      <pubDate>Tue, 23 May 2023 20:10:06 +0000</pubDate>
      <link>https://dev.to/peter_chege79/how-to-migrate-your-database-in-android-with-room-while-also-testing-your-migrations-2jgg</link>
      <guid>https://dev.to/peter_chege79/how-to-migrate-your-database-in-android-with-room-while-also-testing-your-migrations-2jgg</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Hello guys 👋👋, welcome back to another article, in our previous article we discussed &lt;br&gt;
&lt;a href="https://medium.com/@peterkagure/running-instrumented-tests-on-github-actions-with-firebase-test-lab-ac17bb36ae9c"&gt;how to run instrumented tests on CI(Github Actions) with firebase test lab&lt;/a&gt;.&lt;br&gt;
In this article, we'll learn how to make migrations to our database when our data schema changes&lt;/p&gt;

&lt;p&gt;If you're a backend developer or rather have interacted with databases at scale, you're probably familiar &lt;br&gt;
with the concept of migrations especially with relational databases like MySQL or PostgresSQL.&lt;/p&gt;
&lt;h2&gt;
  
  
  What are migrations
&lt;/h2&gt;

&lt;p&gt;Database migrations, also known as schema migrations, database schema migrations, or simply &lt;br&gt;
migrations, are controlled sets of changes developed to modify the structure of the &lt;br&gt;
objects within a relational database. Migrations help transition database schemas from&lt;br&gt;
their current state to a new desired state, whether that involves adding tables and &lt;br&gt;
columns, removing elements, splitting fields, or changing types and constraints.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why migrations
&lt;/h2&gt;

&lt;p&gt;When you building app that stores its data on a remote server, the data is probably stored on a database. &lt;br&gt;
As you continue building your app, the needs of the app change over time which i.e more data needs to be stored or &lt;br&gt;
removed. Whatever the case, migrations help us make us adjust our database to keep it updated with the latest data needs&lt;/p&gt;
&lt;h2&gt;
  
  
  Room in android
&lt;/h2&gt;

&lt;p&gt;In android, we usually use the room library to interact with an SQLite database to model our data for offline &lt;br&gt;
capabilities.As an android developer when you're working on a project then you make changes to an entity data class and &lt;br&gt;
run the app it will probably crash that if you had installed a previous version of the app that had some data in the &lt;br&gt;
database. You will probably see an error message saying that &lt;code&gt;room cannot validate the data integrity .....etc&lt;/code&gt;.&lt;br&gt;
A solution to this is to delete the app on your emulator/physical device and install it again. This will automatically&lt;br&gt;
delete the data and the crash will go away. This works but what if your app has active users who have data stored,if you ship&lt;br&gt;
the app to production and the users download the new version they will experience crashes before even they are able to &lt;br&gt;
interact with the app. Migrations will help us avoid such scenarios&lt;/p&gt;
&lt;h2&gt;
  
  
  How to get started with migrations
&lt;/h2&gt;

&lt;p&gt;For this article I prepared a small project to demostrate this concept&lt;br&gt;
You can find it on GitHub &lt;a href="https://github.com/chege4179/RoomMigrationApp"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The project has 2 branches&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;before-migration - This is before we apply the migration&lt;/li&gt;
&lt;li&gt;after-migration - This is after we apply the migration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project is a simple app that lists people.The project uses Koin for DI as it is lightweight. For the sake of simplicity, I added a button that &lt;br&gt;
will automatically generate the data for us and display it to simulate the addition of a person&lt;br&gt;
The data class of the Person entity is as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity(tableName = "person")
data class PersonEntity(
    @PrimaryKey
    val personId:String,
    val firstName:String,
    val lastName:String
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suppose we would like to add a field called email, we could add it then run our app but as mentioned earlier &lt;br&gt;
the app will crash if there was existing data. To modify the data class we will just add the email field as shown&lt;br&gt;
below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity(tableName = "person")
data class PersonEntity(
    @PrimaryKey
    val personId:String,
    val firstName:String,
    val lastName:String,
    @ColumnInfo(defaultValue = "N/A")
    val email:String,
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We add the &lt;code&gt;ColumnInfo&lt;/code&gt; annotation to define a default value since this is a new field so that room can resolve it to the &lt;br&gt;
default value when the email is not found.&lt;/p&gt;

&lt;p&gt;To start applying the migration, we modify the Database class as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
// Database before migration 
@Database(
    entities = [
        PersonEntity::class,
    ],
    version = 1,
    exportSchema = true
)
abstract class RoomMigrationAppDatabase : RoomDatabase() {
    abstract val personDao: PersonDao;
}

// Database after migration
@Database(
    entities = [
        PersonEntity::class,
    ],
    version = 2,
    exportSchema = true,
    autoMigrations = [
        AutoMigration (from = 1, to = 2)
    ]
)
abstract class RoomMigrationAppDatabase : RoomDatabase() {
    abstract val personDao: PersonDao;
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see we increment the database version, then we add the autoMigrations block to migrate the database &lt;br&gt;
N/B: Make sure you have &lt;code&gt;exportSchemas=true&lt;/code&gt; and have a schemas directory under your app module so that room can &lt;br&gt;
generate the necessary SQL for your database.The schemas will help us when testing the database migrations&lt;/p&gt;

&lt;p&gt;That's just about it really.&lt;/p&gt;

&lt;p&gt;Note that this is a simple migration, for a real world app you'd probably need more complex migrations.&lt;br&gt;
Check the android documentation &lt;a href="https://developer.android.com/training/data-storage/room/migrating-db-versions#kts"&gt;here&lt;/a&gt; &lt;br&gt;
for such.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing our migrations
&lt;/h2&gt;

&lt;p&gt;Migrations are sensitive concept and if not done right could lead to a lot of crashes amongst your users if not done &lt;br&gt;
correctly. That is why we will test our migrations to ensure they run correctly&lt;/p&gt;

&lt;p&gt;Before we start make sure you add the following the build.gradle.kts (App Module)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;android {
    ....
    sourceSets {
        // Adds exported schema location as test app assets.
        getByName("androidTest").assets.srcDir("$projectDir/schemas")
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note I'm using kotlin DSL, if you are using groovy make sure to confirm the syntax.&lt;br&gt;
Above, we are just making sure the exported schemas are used test assets in the androidTest sourceSet&lt;/p&gt;

&lt;p&gt;Then we add the room testing dependency&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    dependencies {
        ..... Other dependencies
        androidTestImplementation("androidx.room:room-testing:2.5.1")
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create a Migration class in the normal source set and add the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Migrations {

    companion object {
        val M1_2: Migration = Migration(startVersion = 1, endVersion = 2) { database -&amp;gt;
            database.execSQL("ALTER TABLE person ADD COLUMN email TEXT")
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will be used to test the migration by executing the given SQL, in the example above we are adding&lt;br&gt;
the email field to the person table&lt;/p&gt;

&lt;p&gt;After that we create MigrationsTest.kt under the androidTest sourceSet and add the following code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RunWith(AndroidJUnit4::class)
class MigrationsTest {


    private val TEST_DB = "room-migration-app-test"

    @get:Rule
    val helper: MigrationTestHelper = MigrationTestHelper(
        InstrumentationRegistry.getInstrumentation(), RoomMigrationAppDatabase::class.java
    )


    @Test
    fun migrate1To2() {
        val db = helper.createDatabase(TEST_DB, 1)
        db.close()
        helper.runMigrationsAndValidate(TEST_DB, 1, true, Migrations.M1_2)
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above, we first declare a migrationTestHelper rule for the test case. The room testing library has a MigrationTestHelper&lt;br&gt;
class to help us test migrations.In the test case, we define we want to test the migration from version 1 to 2&lt;br&gt;
The test case will first create the database at version 1 then migrate it to version 2 as per the SQL we define above.&lt;/p&gt;

&lt;p&gt;If the test case passes, your migration was successful.You can now proceed with your app&lt;/p&gt;

&lt;p&gt;Its worth noting that when you modify your entity data classes, you should just keep on incrementing the database version &lt;br&gt;
while also adding tests to validate those migrations before shipping your app to users&lt;/p&gt;

&lt;p&gt;For more information, check the official documentation &lt;a href="https://developer.android.com/training/data-storage/room/migrating-db-versions"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also don't forget the code from the snippets above is available &lt;a href="https://github.com/chege4179/RoomMigrationApp"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope you enjoyed the article and learnt something new.&lt;/p&gt;

&lt;p&gt;I hope to see you next time, bye. 👋👋👋&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Running Instrumented Tests on Github Actions with Firebase Test Lab</title>
      <dc:creator>Peter Chege</dc:creator>
      <pubDate>Mon, 08 May 2023 19:21:23 +0000</pubDate>
      <link>https://dev.to/peter_chege79/running-instrumented-tests-on-github-actions-with-firebase-test-lab-npb</link>
      <guid>https://dev.to/peter_chege79/running-instrumented-tests-on-github-actions-with-firebase-test-lab-npb</guid>
      <description>&lt;p&gt;Hello guys welcome back to another article, In the previous article we learnt about &lt;a href="https://medium.com/@peterkagure/generating-ui-state-in-android-for-jetpack-compose-using-sealedx-241b52fd5d95"&gt;generating UI state &lt;br&gt;
in android&lt;/a&gt;.&lt;br&gt;
In today's article we will learn how to run instrumentation tests on GitHub actions via&lt;br&gt;
Firebase Test Lab&lt;/p&gt;

&lt;p&gt;Normally when we set up CI/CD pipelines we usually follow 3 general steps&lt;br&gt;
namely: Build,Test and Deploy.&lt;br&gt;
In android development we have different type of tests&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit Test&lt;/li&gt;
&lt;li&gt;Instrumented Test &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unit Test usually run on the JVM and only test a single portion of your code &lt;br&gt;
i.e a class or a function&lt;/p&gt;

&lt;p&gt;Instrumentation tests usually run on an android device (Physical or Emulator).These&lt;br&gt;
tests usually need specify android components to run hence the need for an android &lt;br&gt;
device&lt;/p&gt;

&lt;p&gt;On a CI/CD platform like github actions running unit tests is usually straight foward since &lt;br&gt;
you just setup the JDK and run the gradle command as shown below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;on:
  push:
    branches: [ main ]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Set up JDK
        uses: actions/setup-java@v1
        with:
          java-version: 11
      - name: Cache gradle
        uses: actions/cache@v1
        with:
          path: ~/.gradle/caches
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
          restore-keys: |
             ${{ runner.os }}-gradle-    

      - name: Run Unit Tests with Gradle
        run: ./gradlew test

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But when it comes to instrumented test it is a bit tricky because we don't have a android emulator &lt;br&gt;
on our CI server.While there are a few solution to this like using this &lt;br&gt;
&lt;a href="https://github.com/marketplace/actions/android-emulator-runner"&gt;action&lt;/a&gt; on a macOS runner &lt;/p&gt;

&lt;p&gt;The solution I am about share makes it so easy &lt;/p&gt;
&lt;h2&gt;
  
  
  In comes Firebase Test Lab
&lt;/h2&gt;

&lt;p&gt;As per the &lt;a href="https://firebase.google.com/docs/test-lab"&gt;description&lt;/a&gt; &lt;br&gt;
, Firebase Test Lab is a cloud-based app testing infrastructure that lets you test your app on a range of devices and configurations,&lt;br&gt;
so you can get a better idea of how it'll perform in the hands of live users.&lt;/p&gt;

&lt;p&gt;Assuming you have an android project in which you have instrumented tests already written you can proceed&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Setup a firebase project
&lt;/h3&gt;

&lt;p&gt;For this you need to set up you android app with a firebase project &lt;br&gt;
For more info on how to get started &lt;a href="https://firebase.google.com/docs/android/setup"&gt;check here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Set up your firebase Test Lab Service Account
&lt;/h3&gt;

&lt;p&gt;For use to use the firebase test lab services we need a service account file to help the service &lt;br&gt;
authenticate us.&lt;br&gt;
To do that we need to go to the Google cloud console &lt;br&gt;
&lt;a href="https://console.cloud.google.com/"&gt;here&lt;/a&gt; and create a service account (Every firebase project is &lt;br&gt;
essentially a Google cloud project). To create a service account to be used in a CI system &lt;br&gt;
check &lt;a href="https://firebase.google.com/docs/test-lab/android/continuous"&gt;here&lt;/a&gt; (In the example above they use Jenkins&lt;br&gt;
but the same can be used with github actions)&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Build your androidTest Apk
&lt;/h3&gt;

&lt;p&gt;Once you have defined a device configuration, you should now build and androidTest apk. The androidTest apk &lt;br&gt;
packages all of our tests defined in the androidTest source set into an apk that will be used to &lt;br&gt;
execute the tests. The androidTest apk is usually upload to firebase test lab and the apk is executed &lt;br&gt;
in the cloud.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Define a device configuration
&lt;/h3&gt;

&lt;p&gt;For use to execute the apk we need a device. We need to specify some device configuration to test firebase test lab&lt;br&gt;
what kind of device we want our apk to run on. Note that you can run your tests on multiple different types &lt;br&gt;
of devices just to make sure you app runs correctly in different devices&lt;/p&gt;

&lt;p&gt;To define a device configuration we create a YAML file in the root of our project&lt;br&gt;
e.g &lt;code&gt;android-device.yml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In there we add the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;android-pixel-4:
  type: instrumentation
  app: app/build/outputs/apk/debug/app-debug.apk
  test: app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk
  device:
    - model: redfin
      version: 30
      locale: 'en'
      orientation: portrait

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example we create a device configuration called &lt;code&gt;android-pixel-4&lt;/code&gt; with the following settings :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;app&lt;/code&gt;- key are a path to the actual debug apk &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Test&lt;/code&gt; - Path to the android test apk &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;device&lt;/code&gt; - Device specific settings

&lt;ul&gt;
&lt;li&gt;Model - Device ID of a device supported on firebase test lab. For more device options check &lt;a href="https://firebase.google.com/docs/test-lab/android/available-testing-devices"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Version - Version of the android OS &lt;/li&gt;
&lt;li&gt;Locale - &lt;/li&gt;
&lt;li&gt;Orientation - Orientation of the device when the tests run&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5 : Run our tests
&lt;/h3&gt;

&lt;p&gt;Now we are done with the setup, let's run our tests&lt;br&gt;
To run our tests on our workflow add the following to your workflow file&lt;br&gt;
We use this &lt;a href="https://github.com/marketplace/actions/firebase-test-lab-action"&gt;action&lt;/a&gt;&lt;br&gt;
to access the firebase test lab APIs in our CI environment&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      - name: Build App with Gradle
        run: ./gradlew build

      - name: Build Android Test APK
        run: ./gradlew assembleAndroidTest

      - name: Run Android tests on Firebase Test Lab
        uses: asadmansr/Firebase-Test-Lab-Action@v1.0
        with:
          arg-spec: 'android-device.yml:android-pixel-4'
        env:
          SERVICE_ACCOUNT: ${{ secrets.FIREBASE_TEST_LAB_SERVICE_ACCOUNT }}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The arg-spec specifies the device we defined in the &lt;code&gt;android-device.yml&lt;/code&gt; appended with the device&lt;br&gt;
settings. We then put the service account in our GitHub action secrets and reference it in the file&lt;/p&gt;

&lt;p&gt;If all is good we should see our test results in the firebase console like this &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H2-PVL1G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dhuqr5iyw/image/upload/v1683572040/blog-posts/firebase-test-lab_koqyjg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H2-PVL1G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dhuqr5iyw/image/upload/v1683572040/blog-posts/firebase-test-lab_koqyjg.jpg" alt="Firebase Test Lab console" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;If for some reason you workflow fails at the firebase test lab stage, check the log messages, they can help in &lt;br&gt;
deciphering the problem but here are a few possible causes :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You wrongly configured the service account i.e. you gave the wrong permissions when creating the service account. 
Go over the instructions again&lt;/li&gt;
&lt;li&gt;You probably defined a wrong path to your apks&lt;/li&gt;
&lt;li&gt;The tests probably were uploaded but failed, Check your tests &lt;/li&gt;
&lt;li&gt;You passed a model id for your device configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you liked this article and I hope it helped you run your instrumentation test more reliably on your CI workflow&lt;/p&gt;

&lt;p&gt;For the example project I used here you can find it on GitHub &lt;br&gt;
&lt;a href="https://github.com/chege4179/ExpenseTrackerApp"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also follow me on &lt;a href="https://github.com/chege4179"&gt;github&lt;/a&gt; and &lt;a href="https://twitter.com/peter__me"&gt;twitter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See you next time 👋👋&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Generating UI state in android for jetpack compose using sealedX library</title>
      <dc:creator>Peter Chege</dc:creator>
      <pubDate>Sun, 07 May 2023 16:20:51 +0000</pubDate>
      <link>https://dev.to/peter_chege79/generating-ui-state-in-android-for-jetpack-compose-using-sealedx-library-751</link>
      <guid>https://dev.to/peter_chege79/generating-ui-state-in-android-for-jetpack-compose-using-sealedx-library-751</guid>
      <description>&lt;p&gt;Hello guys, welcome back .In the previous article,&lt;br&gt;
we learnt about &lt;a href="https://medium.com/@peterkagure/stateful-and-stateless-components-in-jetpack-compose-18f89f790750"&gt;stateful and stateless composables&lt;/a&gt; &lt;br&gt;
and how to use them. In this article, we are going to learn how to generate UI states to avoid repetition&lt;/p&gt;

&lt;p&gt;In android, we normally wrap our UI state in data classes to have a single point of reference when is comes to&lt;br&gt;
modifying the state. A common type of state we use when building our app is as shown below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
data class HomeScreenState(
        val msg: String = "",
        val posts: List&amp;lt;Post&amp;gt; = emptyList(),
        val success: Boolean = false,
        val errorMessage: String = "",
        val isLoading: Boolean = false,


        )
private val _state = mutableStateOf(HomeScreenState())
val state: State&amp;lt;HomeScreenState&amp;gt; = _state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data class above wraps all our UI state in one class.The state here is for a screen that &lt;br&gt;
fetches data from a remote API and displays the result on the screen. If your app is mostly presentational and&lt;br&gt;
depends on data from remote APIs, Then most of your screen states look like this.&lt;br&gt;
These types of data classes become repetitive, and we don't like repetitive code so what should we do ?&lt;/p&gt;
&lt;h2&gt;
  
  
  IN comes the sealedX library
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/skydoves/sealedx"&gt;SealedX&lt;/a&gt; is a kotlin library by &lt;a href="https://github.com/skydoves"&gt;github_skydoves&lt;/a&gt; &lt;br&gt;
that generates data classes based of a given input. This library helps us prevent those repetitive data classes by &lt;br&gt;
generating them for us at build time. The library uses KSP to generate the necessary code for us &lt;/p&gt;
&lt;h3&gt;
  
  
  How do we use it ?
&lt;/h3&gt;

&lt;p&gt;First of all make sure to follow the installation steps defined in the project &lt;a href="https://github.com/skydoves/sealedx/blob/main/README.md"&gt;readme&lt;/a&gt;&lt;br&gt;
As you may have already read, there are 2 annotations &lt;code&gt;ExtensiveSealed&lt;/code&gt; and &lt;code&gt;ExtensiveModel&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ExtensiveSealed&lt;/code&gt; is an annotation that is used to anotate the sealed interface for our general  UI state &lt;br&gt;
The most common example something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sealed interface UiState {
  data class Success(val data: Extensive) : UiState
  object Loading : UiState
  object Error : UiState
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When fetching data for a screen these are usually the 3 states we work with&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ExtensiveSealed&lt;/code&gt; annotation usually takes an array of parameters for the classes that need UI state to be generated &lt;br&gt;
as shown below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ExtensiveSealed(
    models = [
        ExtensiveModel(HomeScreen::class),


    ]
)
sealed interface UiState {
data class Success(val data: Extensive) : UiState
object Loading : UiState
object Error (val message:String): UiState


data class HomeScreen(
    val posts:List&amp;lt;Post&amp;gt;
)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above the HomeScreen data class contains the expected data which is passed to the Extensive generic type&lt;br&gt;
Note :  The keyword &lt;code&gt;Extensive&lt;/code&gt; is from the library and is used as a placeholder for the given data class&lt;br&gt;
provided in the models array.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ExtensiveModel&lt;/code&gt; keyword takes a class definition as an argument and is passed &lt;br&gt;
as an element to the models arrays in the &lt;code&gt;ExtensiveSealed&lt;/code&gt; annotation&lt;/p&gt;

&lt;p&gt;So what this does it generates a sealed Interface for each &lt;code&gt;ExtensiveModel&lt;/code&gt; declaration like below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
public sealed interface HomeScreenUiState {
    public data class Error(
    public val message: String,
    ) : HomeScreenUiState

    public object Loading : HomeScreenUiState

    public data class Success(
        public val `data`: HomeScreen,
    ) : HomeScreenUiState
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The name of the sealed interface will be name of the class passed to the &lt;code&gt;ExtensiveModel&lt;/code&gt; appended with the name of the general&lt;br&gt;
sealed interface e.g. HomeScreen + UiState = HomeScreenUiState&lt;/p&gt;

&lt;p&gt;If you pass an array of 3 &lt;code&gt;ExtensiveModel&lt;/code&gt; instances , 3 sealed interfaces will be generated&lt;br&gt;
and so on &lt;/p&gt;

&lt;p&gt;Once you have declared you finshed declaring the &lt;code&gt;ExtensiveSealed&lt;/code&gt; and  &lt;code&gt;ExtensiveModel&lt;/code&gt;, its now time to generate them&lt;br&gt;
To generate the classes , just rebuild your project and you will see the generated classes in your project &lt;/p&gt;
&lt;h3&gt;
  
  
  Let's start using our generated classes
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@HiltViewModel
class HomeScreenViewModel @Inject constructor(
    private val postRepository: PostRepository,
    ) : ViewModel() {

    private val _uiState = MutableStateFlow&amp;lt;HomeScreenUiState&amp;gt;(HomeScreenUiState.Loading)
    val uiState = _uiState.asStateFlow()


    init {
        getPosts()

    }


    private fun getPosts() {
        viewModelScope.launch {
            _uiState.value = HomeScreenUiState.Loading
            try {
                val response = productRepository.getAllProducts()
                _uiState.value = HomeScreenUiState.Success(data = HomeScreen(posts = response.posts))

            } catch (e: HttpException) {
                _uiState.value = HomeScreenUiState.Error(message = "Please check your internet connection")


            } catch (e: IOException) {
                _uiState.value = HomeScreenUiState.Error(message = "Server down please try again later")
            }
        }
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Above we just create our view model and fetch the data from our repository and store it into state &lt;br&gt;
We then consume our state in our composable like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun HomeScreen(
navController: NavController,
navHostController: NavController,
viewModel: HomeScreenViewModel = hiltViewModel(),
) {
    val uiState = viewModel.uiState.collectAsStateWithLifecycle()

    HomeScreenContent(uiState = uiState.value)




}

fun HomeScreenContent(
    uiState:HomeScreenUiState
){
    Box(modifier = Modifier.fillMaxSize()) {
        when (uiState) {
            is ProductsUiState.Loading -&amp;gt; {
                // show something when loading


            }
            is ProductsUiState.Error -&amp;gt; {
                // show the error message


            }
            is ProductsUiState.Success -&amp;gt; {
                // show the posts

            }

        }
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above we just consume the data and show something based on the current state&lt;br&gt;
I am using 2 composable. The reason is explained in this previous article &lt;br&gt;
&lt;a href="https://medium.com/@peterkagure/stateful-and-stateless-components-in-jetpack-compose-18f89f790750"&gt;here&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations of this library
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Currently, the library has a bug where when you try to define more than one sealed interfaces with the
library it will append combine all the states from all the sealed interface instances annotated with the &lt;code&gt;ExtensiveSealed&lt;/code&gt;
keyword into the generated files hence it will cause a compilation error if you declare the same state in more than one instance
I opened an issue in the repo hoping it will be fixed but until then try to only declare one instance annotated with the &lt;code&gt;ExtensiveSealed&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;N/B: Note this is only a solution for when it comes to data fetching for multiple screens. If your app has complex states &lt;br&gt;
then this might not be ideal for your project &lt;/p&gt;

&lt;p&gt;Thank you for reading and I hope you learnt something new until next time .....Bye see you next time&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Stateful and stateless components in Jetpack compose</title>
      <dc:creator>Peter Chege</dc:creator>
      <pubDate>Sun, 30 Apr 2023 14:08:02 +0000</pubDate>
      <link>https://dev.to/peter_chege79/stateful-and-stateless-components-in-jetpack-compose-1cmf</link>
      <guid>https://dev.to/peter_chege79/stateful-and-stateless-components-in-jetpack-compose-1cmf</guid>
      <description>&lt;h4&gt;
  
  
  Keep 2 versions of the same composable:Stateless and Stateful
&lt;/h4&gt;

&lt;h4&gt;
  
  
  What are stateful and stateless composables in Jetpack compose ?
&lt;/h4&gt;

&lt;p&gt;A stateless component is a component that doesn't maintain any internal state. &lt;br&gt;
It's purely a function of its inputs, meaning that the output of the component is solely determined by the props that are passed to it. &lt;br&gt;
These components are also known as "dumb components" because they don't have any logic beyond rendering what &lt;br&gt;
they're given.&lt;/p&gt;

&lt;p&gt;On the other hand, a stateful component is a component that does maintain an internal state.&lt;br&gt;
It's a function of its inputs as well as its own internal state. &lt;br&gt;
Stateful components are also known as "smart components" because they have &lt;br&gt;
logic beyond rendering what they're given. They can respond to user events, &lt;br&gt;
perform complex calculations, and modify their own state, which can trigger a re-render of the component.&lt;/p&gt;

&lt;h5&gt;
  
  
  An example of a stateful Composable
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }

    Column {
        Text("Count: $count")
        Button(onClick = { count++ }) {
            Text("Increment")
        }
    }
}

@Composable
fun HomeScreen(
    viewModel:HomeScreenViewModel = hiltViewModel(),
) {
    val posts = viewModel.posts.collectAsStateWithLifecycle()
    LazyColumn(
        modifier = Modifier.fillMaxSize()
    ){
        items(items =posts){ post -&amp;gt; 
            Post(post = post)
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the 1st composable the state is stored in the composable.&lt;br&gt;
This is said to be stateful since the composable stores its current state&lt;/p&gt;

&lt;p&gt;In the 2nd composable the state is stored in a viewModel. Normally this is what you&lt;br&gt;
usually do in  project &lt;/p&gt;

&lt;p&gt;While this approach works fine there are a few issues that arise &lt;br&gt;
They include&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Testing: Testing stateful composables can be more difficult because they require you to manage and manipulate their internal state. This can make it harder to write tests that are reliable and cover all possible scenarios.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They break previews: Stateful composables cannot be easily previewed in &lt;br&gt;
development mode since they for them to be initialized the need a customview model instance&lt;br&gt;
which will in turn lead to a custom repository for the view model and custom mock data &lt;br&gt;
for the data sources (local or remote etc)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  What is the solution then  ?
&lt;/h4&gt;

&lt;p&gt;In comes stateless composables. As discussed earlier stateless composables do not manage state they simply get &lt;br&gt;
state passed on to them. When the UI needs to make changes we can pass them to the necessary functions as parameters&lt;/p&gt;

&lt;h5&gt;
  
  
  But we will need to eventually instantiate the view model right ?
&lt;/h5&gt;

&lt;p&gt;Yes. We will need 2 different versions of the same composable, A stateless and stateful one then &lt;br&gt;
we use the stateless one for testing and previews.&lt;/p&gt;

&lt;p&gt;Here's an example&lt;/p&gt;

&lt;h5&gt;
  
  
  An example of a stateful Composable
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Stateful version of the composable
@Composable
fun HomeScreen(
    viewModel:HomeScreenViewModel = hiltViewModel(),
) {
    val posts = viewModel.posts.collectAsStateWithLifeCycle()
    HomeScreenContent(posts = posts.value)
}

//Stateless version of the composable
@Composable
fun HomeScreenContent(
    posts:List&amp;lt;Post&amp;gt;
) {
    LazyColumn(
        modifier = Modifier.fillMaxSize()
    ){
        items(items = posts){ post -&amp;gt; 
            Post(post = post)
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above is the stateful and stateless version of the composable. It a good practice to prefix &lt;br&gt;
the stateless composable with "Content" then pass it to the stateful version and pass the state&lt;br&gt;
and the state modifiers functions for example in this case you could add a like post function and pass its &lt;br&gt;
implementation to the HomeScreenContent composable.&lt;/p&gt;

&lt;p&gt;NB: This should only to apply to screen level composables since the other composables in the &lt;br&gt;
screen composable are already statelss &lt;/p&gt;

&lt;p&gt;I recently learnt about this new concept and decided to share it &lt;br&gt;
For more examples check how it was implemented in the following repos&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/chege4179/ExpenseTrackerApp"&gt;Expense Tracker App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/android/nowinandroid"&gt;Now In Android&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Philip Lackner also did a video addressing the same issue &lt;br&gt;
&lt;a href="https://youtu.be/4D79It7Jnzg"&gt;Video Link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this article helped you in a way :)&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
