<?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: Jorge Castro</title>
    <description>The latest articles on DEV Community by Jorge Castro (@jorgecastro).</description>
    <link>https://dev.to/jorgecastro</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%2F419378%2F19b02b8e-245d-4b4c-b42d-d9b2ab9fc4ff.png</url>
      <title>DEV Community: Jorge Castro</title>
      <link>https://dev.to/jorgecastro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jorgecastro"/>
    <language>en</language>
    <item>
      <title>Triggering Jenkins Jobs Remotely via API with Ease</title>
      <dc:creator>Jorge Castro</dc:creator>
      <pubDate>Sat, 24 Aug 2024 23:53:41 +0000</pubDate>
      <link>https://dev.to/jorgecastro/triggering-jenkins-jobs-remotely-via-api-with-ease-20b</link>
      <guid>https://dev.to/jorgecastro/triggering-jenkins-jobs-remotely-via-api-with-ease-20b</guid>
      <description>&lt;p&gt;👋 Hello everyone! In this article, I’ll guide you through the process of remotely triggering Jenkins builds with ease. Whether you’re automating deployments 🚀, integrating with other tools 🔗, or simply looking to streamline your workflow 🛠️, knowing how to execute Jenkins jobs remotely can be a powerful addition to your toolkit. Let’s dive into the steps and best practices to make this process as smooth and efficient as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a New User
&lt;/h3&gt;

&lt;p&gt;From the Jenkins dashboard, click on Manage Jenkins on the left side.&lt;br&gt;
In the Manage Jenkins page, click on Users option.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftdhzz7npuszsegwd3ft5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftdhzz7npuszsegwd3ft5.png" alt="Image 1" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fslyhaohci4f5r1tbvwy5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fslyhaohci4f5r1tbvwy5.png" alt="Image 2" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on &lt;code&gt;+ Create User&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fill out the required fields:

&lt;ul&gt;
&lt;li&gt;Username: The desired username for the new user.&lt;/li&gt;
&lt;li&gt;Password: Enter a strong password.&lt;/li&gt;
&lt;li&gt;Confirm Password: Re-enter the password.&lt;/li&gt;
&lt;li&gt;Full Name: The full name of the user.&lt;/li&gt;
&lt;li&gt;Email Address: The email address of the user.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;Create User&lt;/code&gt; to finish creating the new user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjndxin0ssnsqfi2dpg9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjndxin0ssnsqfi2dpg9.png" alt="Image 3" width="800" height="503"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Generate an API Token
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;After the user is created, log in as the new user or continue if you are still logged in as an admin.&lt;/li&gt;
&lt;li&gt;Click on your username in the upper right corner of the Jenkins dashboard to open the options menu and select &lt;code&gt;Security&lt;/code&gt;. You can also 3. click on the username to go to the user profile where you will find the same options.&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;Add new Token&lt;/code&gt;
Enter a name for the token to identify it (e.g., “My API Token”).&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;Generate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A new API token will be generated. Copy the token and save it in a secure location. (Note: Copy this token now, because it cannot be recovered in the future)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylsxh2uqvl2jkzfoi4h3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylsxh2uqvl2jkzfoi4h3.png" alt="Image 4" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Use the API Token
&lt;/h3&gt;

&lt;p&gt;You can now use this API token as a password when making API calls to Jenkins. Combine it with your username to authenticate requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl JENKINS_URL/job/JOB_NAME/buildWithParameters &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user&lt;/span&gt; USER:TOKEN &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="nv"&gt;param1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;123 &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="nv"&gt;param2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another example — sending a File Parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl JENKINS_URL/job/JOB_NAME/buildWithParameters &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user&lt;/span&gt; USER:PASSWORD &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--form&lt;/span&gt; &lt;span class="nv"&gt;FILE_LOCATION_AS_SET_IN_JENKINS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@PATH_TO_FILE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s take a quick look at an example. In this case, we have a job called &lt;code&gt;Android Regression&lt;/code&gt; that we want to run remotely via the API using &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqgxer4meq8pgfgi6nxw3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqgxer4meq8pgfgi6nxw3.png" alt="Image 5" width="800" height="251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What we will do is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://JENKINS_SERVER_IP:JENKINS_PORT/job/Android%20Regression/buildWithParameters &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user&lt;/span&gt; USER:USER_TOKEN &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="nv"&gt;param1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;value1 &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="nv"&gt;param2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;value2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command above triggers the Jenkins build asynchronously using POST, without retrieving an output response. There’s no need to specify the request type with &lt;code&gt;-X POST&lt;/code&gt; since curl automatically infers it when parameters are passed with &lt;code&gt;--data&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can also add the &lt;code&gt;-i&lt;/code&gt; option, which tells &lt;code&gt;curl&lt;/code&gt; to include the HTTP headers in the response output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://JENKINS_SERVER_IP:JENKINS_PORT/job/Android%20Regression/buildWithParameters &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user&lt;/span&gt; USER:USER_TOKEN &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="nv"&gt;param1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;value1 &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="nv"&gt;param2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;value2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By running the command above, you’ll receive a response in the output with headers that provide relevant information. Below is an example of what the response might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.1 201 Created
Date: Sat, 17 Aug 2024 18:32:49 GMT
X-Content-Type-Options: nosniff
Location: https://JENKINS_SERVER_IP:JENKINS_PORT/queue/item/ITEM_ID/
Vary: Accept-Encoding
Content-Length: 0
Server: Jetty(10.0.21)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the most important header to note is Location: &lt;code&gt;https://JENKINS_SERVER_IP:JENKINS_PORT/queue/item/ITEM_ID/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This header is significant because the URL returned in the Location field points to the item in the Jenkins queue. You can use this link to check the status of the job in the queue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj39v0shfjc2gmtxaew0j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj39v0shfjc2gmtxaew0j.png" alt="Divider" width="800" height="4"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s see how we can retrieve information about the Jenkins build once it’s created. We’ll use a local instance (127.0.0.1:8080) for this part of the tutorial. Here’s how you can do it: once you have the headers from the build execution, we’ll request the build information. Let’s get started!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhi2ma0h7hwdx6d8m7tjp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhi2ma0h7hwdx6d8m7tjp.png" alt="Image 6" width="800" height="152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, we need to request a crumb to authorize our request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-u&lt;/span&gt; USER_NAME:USER_TOKEN &lt;span class="s2"&gt;"http://127.0.0.1:8080/crumbIssuer/api/json"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything is set up correctly, you should receive a response like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"_class"&lt;/span&gt;:&lt;span class="s2"&gt;"hudson.security.csrf.DefaultCrumbIssuer"&lt;/span&gt;,&lt;span class="s2"&gt;"crumb"&lt;/span&gt;:&lt;span class="s2"&gt;"CRUMB_VALUE"&lt;/span&gt;,&lt;span class="s2"&gt;"crumbRequestField"&lt;/span&gt;:&lt;span class="s2"&gt;"Jenkins-Crumb"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This response includes the &lt;code&gt;crumb&lt;/code&gt; (CSRF protection token) and the field name used to include the crumb in subsequent requests.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The crumb, or CSRF (Cross-Site Request Forgery) token, is used to protect systems from cross-site request forgery attacks. In the context of Jenkins, the crumb helps prevent attackers from executing unauthorized requests on behalf of an authenticated user.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, to retrieve information about the generated build from the headers obtained earlier (specifically from the Location header), use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--user&lt;/span&gt; USER_NAME:USER_TOKEN &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Jenkins-Crumb: CRUMB_VALUE"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"http://127.0.0.1:8080/queue/item/item_id/api/json"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command includes the CSRF crumb in the header to authorize the request and fetch the build details.&lt;/p&gt;

&lt;p&gt;If everything goes well, you will see a response like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"_class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hudson.model.Queue$LeftItem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"actions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"_class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hudson.model.ParametersAction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"_class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hudson.model.StringParameterValue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"param1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value1"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"_class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hudson.model.StringParameterValue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"param2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value2"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"_class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hudson.model.CauseAction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"causes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"_class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hudson.model.Cause$UserIdCause"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"shortDescription"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Started by user Automation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"userName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Test"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"blocked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"buildable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"inQueueSince"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1723933184774&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;000aparam1=value1&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;000aparam2=value2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"stuck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"_class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"org.jenkinsci.plugins.workflow.job.WorkflowJob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Android Regression"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:8080/job/Android%20Regression/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"red_anime"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"queue/item/51/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"why"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cancelled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"executable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"_class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"org.jenkinsci.plugins.workflow.job.WorkflowRun"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:8080/job/Android%20Regression/35/"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.jenkins.io/doc/book/using/remote-access-api/" rel="noopener noreferrer"&gt;Remote Access API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj39v0shfjc2gmtxaew0j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj39v0shfjc2gmtxaew0j.png" alt="Divider" width="800" height="4"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;If you like my content and want to support my work, you can give me a cup of coffee ☕️ 🥰&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/devjorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj18vjijbegi1dhp83ozv.png" alt="Ko-fi" width="300" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/jorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjdz714gpdbxv6ve001hf.png" alt="Buy me a coffee" width="299" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow me in
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;X:&lt;/strong&gt; &lt;a href="https://x.com/devjcastro" rel="noopener noreferrer"&gt;@devjcastro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Linkedin:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/devjcastro" rel="noopener noreferrer"&gt;devjcastro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>jenkins</category>
      <category>devops</category>
      <category>cicd</category>
      <category>automation</category>
    </item>
    <item>
      <title>KotlinConf ’23 Recap</title>
      <dc:creator>Jorge Castro</dc:creator>
      <pubDate>Tue, 18 Apr 2023 10:27:18 +0000</pubDate>
      <link>https://dev.to/jorgecastro/kotlinconf-23-recap-232k</link>
      <guid>https://dev.to/jorgecastro/kotlinconf-23-recap-232k</guid>
      <description>&lt;p&gt;KotlinConf is JetBrains’ main event for developers interested in the Kotlin programming language and related technologies. It includes talks, workshops, and networking opportunities. For ’23, the event was held in Amsterdam.&lt;/p&gt;

&lt;p&gt;KotlinConf lets us see once again how the number of Kotlin repositories on Github has been consistently growing significantly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ITVVqJlu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2974/0%2AgKKI09dHggZ0ziNx" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ITVVqJlu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2974/0%2AgKKI09dHggZ0ziNx" alt="Image of Kotlin growth in Github repositories" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Kotlin Everywhere!
&lt;/h3&gt;

&lt;p&gt;Is it true that Kotlin’s great adoption has been largely due to Android? In the following graph we can get an idea of the usage proportions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iYRZk5MI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2284/1%2A1Ggqi51QoJSGnihHNWPjBw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iYRZk5MI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2284/1%2A1Ggqi51QoJSGnihHNWPjBw.png" alt="Image of Kotlin Everywhere" width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kotlin is in 95% of the most used apps in the Play Store 🙀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--udO2glsE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2A3zSKQ9I9nsR9_x3q" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--udO2glsE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2A3zSKQ9I9nsR9_x3q" alt="Image of kotlin percentage on Play Store" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Google uses Kotlin extensively in its projects. As indicated in this article, 45% of its engineers use Kotlin and are creating integrations for all their tools so that they can be used with Kotlin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a-_R8w_A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2858/0%2A989ijGjJ2x9pG433" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a-_R8w_A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2858/0%2A989ijGjJ2x9pG433" alt="Image of Kotlin at Google" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kotlin 2.0&lt;/strong&gt; is getting closer with performance improvements in compilation times. We will have a next version 1.9 and then we will have &lt;strong&gt;version&lt;/strong&gt; 1̶.̶1̶0̶ &lt;strong&gt;2.0.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The kotlin team has taken the feedback from the community very seriously and has been working on a new architecture for &lt;a href="https://blog.jetbrains.com/kotlin/2023/02/k2-kotlin-2-0/"&gt;K2 Compiler&lt;/a&gt; that reduces the bottleneck presented in the compiler frond end.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nfemT-yp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2430/1%2AzahkWHPzccs2yK8bAgp-JQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nfemT-yp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2430/1%2AzahkWHPzccs2yK8bAgp-JQ.png" alt="Image of K2 performance" width="800" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The frontend is the part of the compiler that parses your code and performs the semantic analysis, data flow analysis, call resolution, and type inference. This is the part of the compiler that you, the developer, interact with the most. It’s also the part of the compiler that continuously runs inside your IDE, reports all error and warning messages, and helps you with tasks like auto completion, semantic-aware inspections, intentions, and refactorings&lt;br&gt;
 — Roman Elizarov&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After approximately one year since the official launch of Jetpack Compose, we already have 23% of the apps in the Play Store using Jetpack Compose.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JDyB1dEo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2010/0%2AMPnpLRWxPjsrQ9-N" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JDyB1dEo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2010/0%2AMPnpLRWxPjsrQ9-N" alt="Image of Jetpack Compose percentage on Play Store" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Kotlin DSL
&lt;/h3&gt;

&lt;p&gt;Groovy will accompany Java in sharing the second place, in the upcoming versions of Android Studio we will have Kotlin DSL by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TMpNLQeb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2048/1%2AATe3CxoICsUXYpEQiLllTQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TMpNLQeb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2048/1%2AATe3CxoICsUXYpEQiLllTQ.png" alt="Image of Kotlin DSL" width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Most Desired Features
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JXNn-GP---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A8mAan7ratt_Tp57zpoiJTw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JXNn-GP---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A8mAan7ratt_Tp57zpoiJTw.png" alt="Image of most desired features" width="703" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Static Extensions
&lt;/h3&gt;

&lt;p&gt;Currently, if we want to add a static function to a class through an extension, it is necessary to have a &lt;strong&gt;companion object&lt;/strong&gt; defined within the class.&lt;/p&gt;

&lt;p&gt;For example:&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="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;MyFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello from Kotlin"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nc"&gt;MyFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Companion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyFile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// empty companion object body&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;But this is not the case when we want to apply it to third-party classes, we don’t have the possibility to do it.&lt;/p&gt;

&lt;p&gt;So suppose that we want to create a static extension for the &lt;strong&gt;File&lt;/strong&gt; class our code would not compile with the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RXB2pVyx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AsI6_KnEn73SoDycEe5EXhw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RXB2pVyx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AsI6_KnEn73SoDycEe5EXhw.png" alt="Image of Example of static functions with error" width="726" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So &lt;strong&gt;static extensions&lt;/strong&gt; provide a solution for those cases using the syntax:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ovdYVIYM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2350/1%2A0ser-SM6RNKzvOW-SHGxsQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ovdYVIYM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2350/1%2A0ser-SM6RNKzvOW-SHGxsQ.png" alt="Image of Example of static functions without error" width="800" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Collection Literals
&lt;/h3&gt;

&lt;p&gt;Collection literals are a commonly utilized characteristic in modern programming languages such as Python, JS, Go, Swift, and Rust, to mention just a few examples. Without collection literals you’ll have to remember, by heart, a separate function to create each type of collection. listOf, mapOf, setOf, intArrayOf, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KXvj6B2Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2310/1%2Aw32faxIlPCNQt3asIkcdqg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KXvj6B2Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2310/1%2Aw32faxIlPCNQt3asIkcdqg.png" alt="Image of Collection Literals" width="800" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Name-Based Destructuring
&lt;/h3&gt;

&lt;p&gt;In Kotlin you can unstructure a class into variables, the problem is very easy to make mistakes when the order of the properties is inverted, for example being the product of a refactor, the kotlin team is working on solving this and it is possible that it will come out after kotlin version 2.0&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9THFrGiP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2316/1%2ASqcl0b0IOWAOgmJpQwYy-Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9THFrGiP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2316/1%2ASqcl0b0IOWAOgmJpQwYy-Q.png" alt="Image of name-based destructuring" width="800" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Context Receivers
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TijGFfNl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2378/1%2AwioBCwAHOrz9Km8tFDZFeA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TijGFfNl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2378/1%2AwioBCwAHOrz9Km8tFDZFeA.png" alt="Image of Context Receivers" width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since its release in the experimental phase in kotlin 1.6.20, a lot of feedback has been obtained and they hope to have a stable version in the first versions of kotlin 2.0&lt;/p&gt;

&lt;p&gt;you can check more &lt;a href="https://github.com/Kotlin/KEEP/blob/master/proposals/context-receivers.md"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Explicit Fields
&lt;/h3&gt;

&lt;p&gt;Explicit Fields solve the problem of creating redundant properties within a class by defining a single property instead of separate mutable and immutable properties.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iFPPG9WY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2250/1%2AMj8LEgdi-cz-RwyET6OuvA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iFPPG9WY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2250/1%2AMj8LEgdi-cz-RwyET6OuvA.png" alt="Without Explicit Fields" width="800" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S7hsmkRj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2242/1%2AScO3DPlGcJzm2mAK8LAsvA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S7hsmkRj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2242/1%2AScO3DPlGcJzm2mAK8LAsvA.png" alt="With Explicit Fields" width="800" height="108"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Kotlin Multiplatform
&lt;/h3&gt;

&lt;p&gt;Kotlin Multiplatform is here to stay! The idea is to have its stable ecosystem by 2024.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Cdnk645L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2ACR3yAE4VgjF5gW36IeqZXw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Cdnk645L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2ACR3yAE4VgjF5gW36IeqZXw.png" alt="Image of Kotlin Multiplatform" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Compose Multiplatform
&lt;/h3&gt;

&lt;p&gt;Two important announcements:&lt;/p&gt;

&lt;p&gt;The first is that Compose for Desktop is already stable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RjwKC5GO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A5bmaJc1ywtBeXJQEVGeyIQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RjwKC5GO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A5bmaJc1ywtBeXJQEVGeyIQ.png" alt="Image of Compose Multiplatform Desktop" width="800" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second is that Compose Multiplatform is now targeting iOS!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--77XqEOMr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AtZzUODHHBoMMGDSOQwCu5A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--77XqEOMr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AtZzUODHHBoMMGDSOQwCu5A.png" alt="Image of Compose Multiplatform iOS" width="800" height="306"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/c4f4SCEYA5Q"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://youtrack.jetbrains.com/issue/KT-43871"&gt;https://youtrack.jetbrains.com/issue/KT-43871&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtrack.jetbrains.com/issue/KT-19627"&gt;https://youtrack.jetbrains.com/issue/KT-19627&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h5RMRYHY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j39v0shfjc2gmtxaew0j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h5RMRYHY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j39v0shfjc2gmtxaew0j.png" alt="Divider" width="800" height="4"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;If you like my content and want to support my work, you can give me a cup of coffee ☕️ 🥰&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/devjorgecastro"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VZz5W1BG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j18vjijbegi1dhp83ozv.png" alt="Ko-fi" width="300" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/jorgecastro"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pdX92cM5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jdz714gpdbxv6ve001hf.png" alt="Buy me a coffee" width="299" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow me in
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Twitter:&lt;/strong&gt; &lt;a href="https://twitter.com/devjcastro"&gt;@devjcastro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Linkedin:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/dev-jorgecastro/"&gt;dev-jorgecastro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kotlin</category>
      <category>mobile</category>
      <category>backend</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Install JetBrains Toolbox on Linux</title>
      <dc:creator>Jorge Castro</dc:creator>
      <pubDate>Sun, 05 Mar 2023 04:38:50 +0000</pubDate>
      <link>https://dev.to/jorgecastro/install-jetbrains-toolbox-on-linux-596n</link>
      <guid>https://dev.to/jorgecastro/install-jetbrains-toolbox-on-linux-596n</guid>
      <description>&lt;p&gt;Hello everyone, Welcome to a small article where we will learn how to install JetBrains Toolbox in our Linux distribution. In my case, I’m using &lt;strong&gt;Fedora 37&lt;/strong&gt;, but I think you won’t have any inconvenience if you use another distribution.&lt;/p&gt;

&lt;p&gt;But why do we need to install JetBrains Toolbox? If I’m an &lt;strong&gt;Android developer&lt;/strong&gt;, do I need to install the JetBrains Toolbox?&lt;/p&gt;

&lt;p&gt;The answers are simple: You do not need to have them installed to be able to work with Android Studio or any other IDE from the JetBrains family. But having JetBrains Toolbox installed gives us a number of advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Easy management of multiple JetBrains tools&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy upgrade of your JetBrains tools&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Quick access to multiple versions of the same tool&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integration with other services such as GitHub and GitLab&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having said that, let’s start with the steps…&lt;/p&gt;

&lt;p&gt;First, we need to download the JetBrains Toolbox App from the &lt;a href="https://www.jetbrains.com/toolbox-app/" rel="noopener noreferrer"&gt;official site&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhg083qqshqnt1gt5nc2i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhg083qqshqnt1gt5nc2i.png" alt="Image by [JetBrains](https://resources.jetbrains.com/storage/products/toolbox/img/meta/preview.png)" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the download folder, you should see a file with a name similar to the following: &lt;strong&gt;jetbrains-toolbox-{version}.tar.gz&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Later, we will uncompress the downloaded file inside the &lt;strong&gt;opt/&lt;/strong&gt; folder, and we will use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo tar&lt;/span&gt; &lt;span class="nt"&gt;-xvzf&lt;/span&gt; ~/Downloads/jetbrains-toolbox-&lt;span class="o"&gt;{&lt;/span&gt;version&lt;span class="o"&gt;}&lt;/span&gt;.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change {version} to the version in your file name. At the moment of creating this article, the current version is &lt;strong&gt;1.27.3.14493&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you want, you could rename the extracted folder the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mv &lt;/span&gt;jetbrains-toolbox-&lt;span class="o"&gt;{&lt;/span&gt;version&lt;span class="o"&gt;}&lt;/span&gt; jetbrains
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ready! Now open JetBrains Toolbox and run your executable with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/opt/jetbrains/jetbrains-toolbox 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furzret6kuda9sh7q5dfw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furzret6kuda9sh7q5dfw.png" alt="Image of JetBrains Toolbox open" width="434" height="696"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Add quick access
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fah992tb2b104goxkn1kf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fah992tb2b104goxkn1kf.png" alt="Image of the Fedora 37 applications menu" width="800" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you simply want to see JetBrains Toolbox in your list of applications, we will perform the following steps:&lt;/p&gt;

&lt;p&gt;First, download the JetBrains Toolbox logo; I leave an alternative &lt;a href="https://iconarchive.com/show/papirus-apps-icons-by-papirus-team/jetbrains-toolbox-icon.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;; then move the image to the &lt;strong&gt;/opt/jetbrains/&lt;/strong&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mv&lt;/span&gt; ~/Downloads/jetbrains-toolbox-icon.png /opt/jetbrains/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we will create a shortcut to be able to start JetBrains Toolbox from the applications menu. To do this, we will create a .desktop file as follows: in your terminal, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vi /usr/share/applications/jetbrains-toolbox.desktop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the newly created file, paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Desktop Entry]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Application&lt;/span&gt;
&lt;span class="py"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;JetBrains Toolbox&lt;/span&gt;
&lt;span class="py"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/jetbrains/jetbrains-toolbox&lt;/span&gt;
&lt;span class="py"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/jetbrains/jetbrains-toolbox-icon.png&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file &lt;strong&gt;:wq&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now you can use the application menu to search for JetBrains Tools.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj39v0shfjc2gmtxaew0j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj39v0shfjc2gmtxaew0j.png" alt="Divider" width="800" height="4"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;If you like my content and want to support my work, you can give me a cup of coffee ☕️ 🥰&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/devjorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj18vjijbegi1dhp83ozv.png" alt="Ko-fi" width="300" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/jorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjdz714gpdbxv6ve001hf.png" alt="Buy me a coffee" width="299" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow me in
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Twitter:&lt;/strong&gt; &lt;a href="https://twitter.com/devjcastro" rel="noopener noreferrer"&gt;@devjcastro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Linkedin:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/dev-jorgecastro/" rel="noopener noreferrer"&gt;dev-jorgecastro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>code</category>
      <category>snippet</category>
    </item>
    <item>
      <title>Update your Android app signing key from a new Keystore with Play App Signing.</title>
      <dc:creator>Jorge Castro</dc:creator>
      <pubDate>Fri, 17 Feb 2023 22:36:52 +0000</pubDate>
      <link>https://dev.to/jorgecastro/update-your-android-app-signing-key-from-a-new-keystore-with-play-app-signing-137h</link>
      <guid>https://dev.to/jorgecastro/update-your-android-app-signing-key-from-a-new-keystore-with-play-app-signing-137h</guid>
      <description>&lt;p&gt;Hello everyone! Today, I would like to talk to you about a topic that is rarely discussed in our work as Android engineers but is one of the most important things we should consider in case the need arises: updating your app signing key. 🔑&lt;/p&gt;

&lt;p&gt;It is important to clarify that this article assumes that you are using Play App Signing; beforehand, it would be ideal to use this feature in the Google Play Console. On the other hand, we are going to carry out the process by generating the new certificate from a new Keystore.&lt;/p&gt;

&lt;h3&gt;
  
  
  Play App Signing
&lt;/h3&gt;

&lt;p&gt;It is a service offered to Android developers by the Google Play Console that allows you to store your app’s signing key in Google’s secure infrastructure and offers upgrade options to increase security.&lt;/p&gt;

&lt;p&gt;See more information &lt;a href="https://support.google.com/googleplay/android-developer/answer/9842756" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  But why would I have to change my signature key?
&lt;/h3&gt;

&lt;p&gt;You should consider changing your signing key if you suffer a loss of it or if it is compromised by people outside your organization. Another scenario would be when the application changes ownership and you want to ensure that only authorized people have access to the key; and finally, if your signing key collides with another App Signing Key.&lt;/p&gt;

&lt;p&gt;First we will create a new Keystore to generate the new app signing key. You can generate a Keystore from Android Studio or from the command line with help of keytool.&lt;/p&gt;

&lt;p&gt;If you want to use Android Studio, check out the Android documentation &lt;a href="https://developer.android.com/studio/publish/app-signing?419#sign-apk" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mc5p3jf7v6ofd3lh5z9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mc5p3jf7v6ofd3lh5z9.png" alt="Modal to create a keystore from android studio" width="800" height="796"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other hand if you want to use the keytool then you could copy the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;keytool &lt;span class="nt"&gt;-genkey&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-keystore&lt;/span&gt; new_keystore.jks &lt;span class="nt"&gt;-alias&lt;/span&gt; new_alias &lt;span class="nt"&gt;-keyalg&lt;/span&gt; RSA &lt;span class="nt"&gt;-keysize&lt;/span&gt; 2048 &lt;span class="nt"&gt;-validity&lt;/span&gt; 10000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A keystore with the specified name and the private key associated with the specified alias will be generated. In the creation process, you will be asked for some details, such as the name and location of the organization, the common name of the issuer, etc.&lt;br&gt;
Now go to the Play App Signing page and select the &lt;strong&gt;App signing tab&lt;/strong&gt;. (&lt;strong&gt;Release&lt;/strong&gt; &amp;gt; &lt;strong&gt;Setup&lt;/strong&gt; &amp;gt; &lt;strong&gt;App integrity&lt;/strong&gt; &amp;gt; &lt;strong&gt;App signing tab&lt;/strong&gt;).&lt;br&gt;
Then, request to update the signing key and select the option Upload a new &lt;strong&gt;app signing key from Java Keystore&lt;/strong&gt; and execute the suggested steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download the &lt;strong&gt;PEPK&lt;/strong&gt; tool&lt;/li&gt;
&lt;li&gt;Copy the command shown in the Play Console and replace only the parameters highlighted in bold&lt;/li&gt;
&lt;li&gt;Once the certificate is generated, upload it to the Play Store Console&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8mih2dbisi63xo4juwlx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8mih2dbisi63xo4juwlx.png" alt="Image to represent the option: Upload a new app signing key from Java Keystore" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PEPK&lt;/strong&gt; tool is an encryption tool provided by Google for protecting private keys of Android applications. PEPK stands for "Private Key Encryption Protection" and is a file format that contains an encrypted private key with a master password.&lt;br&gt;
Let's explain a little more detailed the command that we must execute. Open your terminal and paste the command copied from the Google Play console&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;java &lt;span class="nt"&gt;-jar&lt;/span&gt; pepk.jar &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--keystore&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;foo.keystore &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--alias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;foo &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;output.zip &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--signing-keystore&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;uploadkey.keystore &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--signing-key-alias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;upload-key-alias &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--encryptionkey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;generated-encryption-key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next I explain what each argument means.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;keystore&lt;/strong&gt;: specifies the location and filename of the new keystore that will be used to sign the encrypted private key output by the PEPK tool. This keystore should contain a private key with the alias specified by the alias parameter.&lt;br&gt;
&lt;strong&gt;alias&lt;/strong&gt;: This is the alias of the private key in the keystore file that will be used in the signed file.&lt;br&gt;
&lt;strong&gt;output&lt;/strong&gt;: This parameter is used to specify the name of the signed file.&lt;br&gt;
&lt;strong&gt;signing-keystore&lt;/strong&gt;: specifies the location and filename of the existing keystore that contains the private key that will be used to sign the APK file after it is processed by the PEPK tool. The keystore specified here should contain a private key with the alias specified by the signing-key-alias parameter.&lt;br&gt;
&lt;strong&gt;signing-key-alias&lt;/strong&gt;: This parameter specifies the alias of the private key in the keystore file that will be used to sign the file.&lt;br&gt;
&lt;strong&gt;encryptionkey&lt;/strong&gt;: Encryption key to use to encrypt the output file.&lt;/p&gt;

&lt;p&gt;📝: &lt;strong&gt;signing-keystore&lt;/strong&gt; and &lt;strong&gt;signing-key-alias&lt;/strong&gt; are parameters that correspond to the keystore that contains the upload key certificate. If by mistake you put in all the parameters only the data of the new keystore you will get the following error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl5dg4iy078gdbf9q11wi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl5dg4iy078gdbf9q11wi.png" alt="Image to represent the error when trying to upload a certificate generated with a wrong upload key" width="800" height="80"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;If everything was done correctly we should be able to see our certificate created&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1tfangsymsk9k7reyslo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1tfangsymsk9k7reyslo.png" alt="Image with the result of the certificate created" width="746" height="468"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;If you like my content and want to support my work, you can give me a cup of coffee ☕️ 🥰&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/devjorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj18vjijbegi1dhp83ozv.png" alt="Ko-fi" width="300" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/jorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjdz714gpdbxv6ve001hf.png" alt="Buy me a coffee" width="299" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow me in
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Twitter:&lt;/strong&gt; &lt;a href="https://twitter.com/devjcastro" rel="noopener noreferrer"&gt;@devjcastro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Linkedin:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/dev-jorgecastro/" rel="noopener noreferrer"&gt;dev-jorgecastro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vibecoding</category>
    </item>
    <item>
      <title>RSS reader in Python for Discord</title>
      <dc:creator>Jorge Castro</dc:creator>
      <pubDate>Sun, 05 Feb 2023 06:17:24 +0000</pubDate>
      <link>https://dev.to/jorgecastro/rss-reader-in-python-for-discord-556n</link>
      <guid>https://dev.to/jorgecastro/rss-reader-in-python-for-discord-556n</guid>
      <description>&lt;p&gt;Hey guys, I hope everyone is having a good day. Today I want to create a short article on how to read RSS feeds with Python and notify a Discord channel using Webhook. First of all I wanted to use some of the Discord bot alternatives on the market, but the free options were very limited for what I wanted 😅, it’s not bad to pay for the use of tools that make your life easier, but honestly I took this as an excuse to do some coding over the weekend. It is my hobby! ❤️.&lt;/p&gt;

&lt;p&gt;Let’s start&lt;/p&gt;

&lt;p&gt;To get started, we need to search for any site with RSS support; in my case i searched for sources for android development and for this example i will use the following URL:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/feed/@devjorgecastro" rel="noopener noreferrer"&gt;https://medium.com/feed/@devjorgecastro&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the project is created, we need to install the **feedparser **library to read rss with python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;feedparser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Next, we’ll create our main.py, add the webhook, username, and avatar url.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;feedparser&lt;/span&gt;

&lt;span class="n"&gt;webhook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://discord.com/webhooks/{webhook_id}/{webhook_token}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Androd News&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;avatar_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://source.android.com/static/docs/setup/images/Android_symbol_green_RGB.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;feed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;feedparser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feed_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If everything is ok, when you run the program you should be able to see a terminal output similar to the following image&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F16xm3ceb9lu2dcfx0et7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F16xm3ceb9lu2dcfx0et7.png" alt="Example of RSS reader result" width="800" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s continue, the next thing we will do is create a class to manage our Discord data and have the ability to launch notifications. We will call our class &lt;strong&gt;discord_module/DiscordNews.py&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DiscordNews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;avatar_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webhook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webhook&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avatar_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;avatar_url&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;feed&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;prepare_and_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__notify_to_discord_channel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__notify_to_discord_channel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Code for notify to Discord channel
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As you may have noticed we have 2 parameters in the class constructor: avatar_url and username, these are used to customize the posting of the message in the Discord channel; with avatar_url we specify a url of an image that will be the icon of our bot and username will be its name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fze68vgwa9jeuc2mpy222.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fze68vgwa9jeuc2mpy222.png" alt="details of the bot’s post on the Discord channel" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next thing is to import our class into &lt;strong&gt;main.py&lt;/strong&gt; and call the function &lt;strong&gt;prepare_and_notify&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;discord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DiscordNews&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;avatar_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;discord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare_and_notify&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Finally, we run the program.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj39v0shfjc2gmtxaew0j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj39v0shfjc2gmtxaew0j.png" alt="Divider" width="800" height="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Result&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdggsz1wckvagcniffo16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdggsz1wckvagcniffo16.png" alt="Example of messages in Discord channel" width="800" height="693"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;See the full code here&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj39v0shfjc2gmtxaew0j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj39v0shfjc2gmtxaew0j.png" alt="Divider" width="800" height="4"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;If you like my content and want to support my work, you can give me a cup of coffee ☕️ 🥰&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/devjorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj18vjijbegi1dhp83ozv.png" alt="Ko-fi" width="300" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/jorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjdz714gpdbxv6ve001hf.png" alt="Buy me a coffee" width="299" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow me in
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Twitter:&lt;/strong&gt; &lt;a href="https://twitter.com/devjcastro" rel="noopener noreferrer"&gt;@devjcastro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Linkedin:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/dev-jorgecastro/" rel="noopener noreferrer"&gt;dev-jorgecastro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>embeds</category>
      <category>darkmode</category>
      <category>twitter</category>
    </item>
    <item>
      <title>Modern Android Development in 2023</title>
      <dc:creator>Jorge Castro</dc:creator>
      <pubDate>Mon, 23 Jan 2023 23:35:35 +0000</pubDate>
      <link>https://dev.to/jorgecastro/modern-android-development-in-2023-1bgm</link>
      <guid>https://dev.to/jorgecastro/modern-android-development-in-2023-1bgm</guid>
      <description>&lt;p&gt;Hello everyone 👋🏻, I’d like to share with you how to build Android apps with the latest trends for the year 2023.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;This is an article from my opinion and professional experience, taking into account different opinions among the Android developer community, also constantly reviewing the guides provided by Google for Android.&lt;/p&gt;

&lt;p&gt;I have to make it clear that there are very interesting tools, patterns and architectures that I may not mention but it does not mean that they cannot be other interesting alternatives to develop Android applications.&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is android?
&lt;/h2&gt;

&lt;p&gt;Android is an open-source operating system based on the Linux kernel and developed by Google. It’s used in a wide variety of devices, including smartphones, tablets, TVs, and smartwatches.&lt;/p&gt;

&lt;p&gt;Currently, Android is the operating system most used in the world for mobile devices; according to a report by &lt;a href="https://gs.statcounter.com/os-market-share/mobile/worldwide/#monthly-202112-202212" rel="noopener noreferrer"&gt;&lt;strong&gt;statcounter&lt;/strong&gt;&lt;/a&gt; with a sample of the last 12 months, Android has a market share of &lt;strong&gt;71.96%.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, I will mention a list of tools, libraries, architectures, guides, and other utilities that I consider important to building modern applications on Android.&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Kotlin ❤️
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F5120%2F0%2ApiQN_I004o_ugTCN.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F5120%2F0%2ApiQN_I004o_ugTCN.jpg" alt="Image by [https://talently.tech/](https://talently.tech/)" width="800" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kotlin is a programming language developed by &lt;a href="https://www.jetbrains.com/" rel="noopener noreferrer"&gt;JetBrains&lt;/a&gt;. Recommended by Google who officially announced it in May 2017 (see publication &lt;a href="https://android-developers.googleblog.com/2017/05/android-announces-support-for-kotlin.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;). It is a modern programming language that has compatibility with Java and can run on the JVM, which has made its adoption in the development of Android applications very fast.&lt;/p&gt;

&lt;p&gt;Whether you are new to Android or not, you should consider Kotlin as your first choice, don’t swim against the tide 🏊🏻 😎, Google &lt;a href="https://developer.android.com/kotlin/first" rel="noopener noreferrer"&gt;announced&lt;/a&gt; this approach at Google I/O 2019. With Kotlin, you will be able to use all the features of a modern language, including the power of Coroutines and the use of modern libraries developed for the Android ecosystem.&lt;/p&gt;

&lt;p&gt;Official kotlin documentation &lt;a href="https://kotlinlang.org/docs/home.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Jetpack Compose 😍
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AkG-9BQIyUm8MblpZ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AkG-9BQIyUm8MblpZ.png" alt="Image by [blogger.googleusercontent.com](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv3CjuEunEpPuvrsKEV0f7R_OSPonuZnDUi97Hrz68T9xKpsUHRoFaivipPxNQ6QT3BIHVxtSH85xurEqxSnC1S7rQklKL8vKj40E2xgFWE9ylhjZDHbWoie3Evgl_WjnG2nQ1UxFVs9lg3IAMIv_CHCsuesUuCW_BMddUXU4JErFXLn9Twcy2tKKfHA/s1600/Android-JetpackCompose1.2-Header.png)" width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Jetpack Compose is Android’s recommended modern toolkit for building native UI. It simplifies and accelerates UI development on Android.&lt;br&gt;
 _ &lt;a href="https://developer.android.com/jetpack/compose" rel="noopener noreferrer"&gt;Jetpack Compose documentation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Jetpack Compose is part of the Android Jetpack library and uses the Kotlin programming language to easily create a native user interface. Also, it integrates with other Android Jetpack libraries, such as LiveData and ViewModel, to make it easier to build reactive and maintainable Android applications.&lt;/p&gt;

&lt;p&gt;Some key features of Jetpack Compose include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Declarative UI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Customizable widgets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy integration with existing code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Live preview.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improved performance.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/jetpack/compose" rel="noopener noreferrer"&gt;Official documentation.&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/jetpack/androidx/releases/compose-kotlin" rel="noopener noreferrer"&gt;Compose to Kotlin Compatibility Map&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/jetpack/androidx/compose-roadmap" rel="noopener noreferrer"&gt;Jetpack Compose Roadmap&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/courses/jetpack-compose/course" rel="noopener noreferrer"&gt;Course.&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://developer.android.com/jetpack/compose" rel="noopener noreferrer"&gt;Jetpack Compose documentation&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Android Jetpack
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2400%2F0%2A3LHozcwxQYiKVhPG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2400%2F0%2A3LHozcwxQYiKVhPG.png" alt="Image by [Florina Muntenescu](https://medium.com/@florina.muntenescu) on [medium](https://medium.com/androiddevelopers/whats-new-in-jetpack-1891d205e136)" width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Jetpack is a suite of libraries to help developers follow best practices, reduce boilerplate code, and write code that works consistently across Android versions and devices so that developers can focus on the code they care about.&lt;br&gt;
 _ &lt;a href="https://developer.android.com/jetpack" rel="noopener noreferrer"&gt;Android Jetpack documentation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some of its most common tools are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/topic/libraries/architecture/viewmodel" rel="noopener noreferrer"&gt;ViewModel&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/training/data-storage/room" rel="noopener noreferrer"&gt;Room&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/topic/libraries/architecture/datastore" rel="noopener noreferrer"&gt;DataStore&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/topic/libraries/architecture/workmanager" rel="noopener noreferrer"&gt;WorkManager&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/guide/navigation" rel="noopener noreferrer"&gt;Navigation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/training/camerax" rel="noopener noreferrer"&gt;CameraX&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Compose&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Material Design
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9nzw9to3ww71bkwqq0u7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9nzw9to3ww71bkwqq0u7.png" alt="Illustration by Reed Hollett" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Material Design is an adaptable system of guidelines, components, and tools that support the best practices of user interface design. Backed by open-source code, Material Design streamlines collaboration between designers and developers, and helps teams quickly build beautiful products.&lt;br&gt;
 _ &lt;a href="https://m3.material.io/" rel="noopener noreferrer"&gt;Material Design Site&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Material Design is backed by designers and developers from Google and will allow us to have a guide to work on our UI/UX for Android, Flutter and Web.&lt;/p&gt;

&lt;p&gt;Currently, the last version for Material Design is 3, you can see more &lt;a href="https://m3.material.io/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Clean Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7bv3z52ugk2rkc4h2xfr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7bv3z52ugk2rkc4h2xfr.jpg" alt="Cover image for Clean Architecture" width="772" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The concept of "&lt;strong&gt;Clean Architecture"&lt;/strong&gt; was introduced by &lt;a href="https://en.wikipedia.org/wiki/Robert_C._Martin" rel="noopener noreferrer"&gt;Robert C. Martin&lt;/a&gt;. It is based on the separation of responsibilities through the division of software into layers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Characteristics&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Independent of Frameworks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Testable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Independent of UI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Independent of Database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Independent of any external agency.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Dependency Rule&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The dependency rule is described very well by the author in his piece, &lt;a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html" rel="noopener noreferrer"&gt;The Clean Code Blog&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The overriding rule that makes this architecture work is &lt;em&gt;The Dependency Rule&lt;/em&gt;. This rule says that &lt;em&gt;source code dependencies&lt;/em&gt; can only point &lt;em&gt;inwards&lt;/em&gt;. Nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in the an inner circle. That includes, functions, classes. variables, or any other named software entity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html" rel="noopener noreferrer"&gt;The Clean Code Blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clean Architecture in Android&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Presentation&lt;/strong&gt;: Activities, Fragments, View Models, others view components.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Domain&lt;/strong&gt;: Use Cases, Entities, Repositories, others domain components.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data&lt;/strong&gt;: Repository implementations, Mappers, DTO’s, etc.&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture Patterns for Presentation Layer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" alt="Cover image for Architecture Patterns for Presentation Layer" width="800" height="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An architecture pattern is a higher-level strategy that aims to help design a software architecture and is characterized by being a solution within a reusable framework for common architectural problems. Architectural patterns are similar to design patterns, but they are larger in scale and address more global issues such as the overall structure of the system, the relationships between components, and the way that data is managed.&lt;/p&gt;

&lt;p&gt;Within the Presentation layer, we have some architecture patterns, of which I would like to highlight the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MVVM&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MVI&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I do not want to go into explaining each one because on the internet you find too much information about this. 😅&lt;/p&gt;

&lt;p&gt;In addition, you can also see the &lt;a href="https://developer.android.com/topic/architecture" rel="noopener noreferrer"&gt;&lt;strong&gt;guide to app architecture&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3394%2F0%2AQJ56TjhdXPcQweAk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3394%2F0%2AQJ56TjhdXPcQweAk.png" alt="Image by [developer.android.com](https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-overview-ui.png)" width="800" height="708"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency Injection
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" alt="Cover image for Dependency Injection" width="800" height="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dependency injection is a software design pattern that allows a client to obtain its dependencies from an external source rather than creating them itself. It is a technique for achieving Inversion of Control (IoC) between objects and their dependencies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/training/dependency-injection/hilt-android" rel="noopener noreferrer"&gt;Hilt&lt;/a&gt; ❤️&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dagger.dev/" rel="noopener noreferrer"&gt;Dagger&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://insert-koin.io/" rel="noopener noreferrer"&gt;Koin&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Modularization
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" alt="Cover image for Modularization" width="800" height="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Modularization is a software design technique that allows you to divide an application into independent modules, each with its own functionality and responsibility.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2ANNUw83lZ228t5yLD.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2ANNUw83lZ228t5yLD.png" alt="Image by [developer.android.com](https://developer.android.com/static/topic/modularization/images/1_sample_dep_graph.png)" width="640" height="244"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of modularization
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Reusability&lt;/strong&gt;: By having independent modules, they can be reused in different parts of the application or even in other applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strict visibility control&lt;/strong&gt;: Modules enable you to easily control what you expose to other parts of your codebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customizable delivery&lt;/strong&gt;: Play Feature Delivery uses the advanced capabilities of app bundles, allowing you to deliver certain features of your app conditionally or on demand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: By having independent modules, functionalities can be added or removed without affecting other parts of the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ease of maintenance&lt;/strong&gt;: By dividing the application into independent modules, each with its own functionality and responsibility, it is easier to understand and maintain the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ease of testing&lt;/strong&gt;: By having independent modules, they can be tested in isolation, which makes it easy to detect and fix errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architecture improvement&lt;/strong&gt;: Modularizing helps to improve the architecture of the application, allowing a better organization and structure of the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improve collaboration&lt;/strong&gt;: By having independent modules, developers can work on different parts of the application simultaneously and without interference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Time&lt;/strong&gt;: Some Gradle functionalities such as incremental build, build cache or parallel build, can leverage modularity to improve build performance.&lt;/p&gt;

&lt;p&gt;See more in the &lt;a href="https://developer.android.com/topic/modularization" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;.&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Network
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" alt="Cover image for Network" width="800" height="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://square.github.io/okhttp/" rel="noopener noreferrer"&gt;OkHttp&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://square.github.io/retrofit/" rel="noopener noreferrer"&gt;Retrofit&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Serialization
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" alt="Cover image for Serialization" width="800" height="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this section I would like to mention two important tools in my opinion: &lt;strong&gt;Moshi&lt;/strong&gt; widely used in conjunction with Retrofit and &lt;strong&gt;Kotlin Serialization&lt;/strong&gt;, the bet of the Kotlin team at Jetbrain.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/square/moshi" rel="noopener noreferrer"&gt;Moshi&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/Kotlin/kotlinx.serialization" rel="noopener noreferrer"&gt;Kotlin Serialization&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Moshi&lt;/strong&gt; and &lt;strong&gt;Kotlin Serialization&lt;/strong&gt; are two serialization/deserialization libraries for Kotlin and Java that allow you to convert objects to JSON or another serialization format and vice versa. Both provide a user-friendly interface optimized for use in mobile and desktop applications. Moshi primarily focuses on JSON serialization, while Kotlin Serialization has support for various serialization formats, including JSON.&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Image Loading
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" alt="Cover image for Image Loading" width="800" height="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To load an image from the internet, there are several third-party libraries available to help you handle the process. Image loading libraries do a lot of the heavy lifting for you; they handle both caching (so you don’t download the image multiple times) and networking logic to download the image and display it on screen.&lt;br&gt;
 _ &lt;a href="https://developer.android.com/jetpack/compose/graphics/images/loading" rel="noopener noreferrer"&gt;Official Android Documentation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/coil-kt/coil#jetpack-compose" rel="noopener noreferrer"&gt;Coil&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://bumptech.github.io/glide/int/compose.html" rel="noopener noreferrer"&gt;Glide&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reactivity / Thread Management
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3pfa2zkg5mfbh5lap8vs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3pfa2zkg5mfbh5lap8vs.png" alt="Cover image for Reactivity / Thread Management" width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we talk about reactive programming and asynchronous processes, our first option is Kotlin Coroutines; thanks to the Suspension Functions and Flow we can cover all these needs. However, I believe that in this section it is worth highlighting the importance of RxJava even within the development of Android applications. For those of us who have been working with Android for a couple of years, we know that RxJava is a very powerful tool with a very large set of functions for working with data streams. I still consider RxJava to be an interesting alternative to consider today.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/Kotlin/kotlinx.coroutines/blob/master/README.md#using-in-your-projects" rel="noopener noreferrer"&gt;Kotlin Coroutines&lt;/a&gt;: &lt;a href="https://dev.toNice%20to%20have%20%E2%9D%A4%EF%B8%8F"&gt;suspend functions / Flow Api &lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/ReactiveX/RxJava" rel="noopener noreferrer"&gt;RxJava&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Local Store
&lt;/h2&gt;

&lt;p&gt;An important point when building mobile applications is having the ability to persist data locally, such as some session data or cache data, among others. It is important to choose the right storage option based on the needs of your application. We could store unstructured data like key-value or structured data like a database. Keep in mind that this point does not mention all the types of local storage that we have available (such as file storage), only the tools that allow us to save data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6k199ccb0c4w6agi4ae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6k199ccb0c4w6agi4ae.png" alt="Cover image for Local Store" width="800" height="691"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Suggestions&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;del&gt;S̶h̶a̶r̶e̶d̶P̶r̶e̶f̶e̶r̶e̶n̶c̶e̶s̶&lt;/del&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/topic/libraries/architecture/datastore" rel="noopener noreferrer"&gt;DataStore&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences" rel="noopener noreferrer"&gt;EncryptedSharedPreferences&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" alt="Cover image for Testing" width="800" height="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://junit.org/junit5/" rel="noopener noreferrer"&gt;JUnit 5&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://mockk.io/ANDROID.html" rel="noopener noreferrer"&gt;Mockk&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/training/testing/espresso" rel="noopener noreferrer"&gt;Espresso&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://robolectric.org/" rel="noopener noreferrer"&gt;Robolectric&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  R8 optimizations
&lt;/h2&gt;

&lt;p&gt;R8 is the default compiler that converts your project’s Java bytecode into the DEX format that runs on the Android platform. It is a tool that helps us to obfuscate and reduce the code of our application by shortening the names of the classes and their properties, eliminating unused code and resources within the project. To see more, check the Android documentation about &lt;a href="https://developer.android.com/studio/build/shrink-code" rel="noopener noreferrer"&gt;Shrink, obfuscate, and optimize your app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpmagdhgimq7m7f8mmzqa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpmagdhgimq7m7f8mmzqa.png" alt="Image cover for R8 optimizations" width="800" height="234"&gt;&lt;/a&gt;&lt;br&gt;
Image by &lt;a href="https://androidtopics.dipien.com/r8-proguard-all-official-development-resources-864e471f6d42" rel="noopener noreferrer"&gt;androidtopics.dipien.com&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Code shrinking&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Resource shrinking&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Obfuscation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optimization&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Play Feature Delivery
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" alt="Cover image for Play Feature Delivery" width="800" height="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Google Play’s app serving model, called Dynamic Delivery, uses Android App Bundles to generate and serve optimized APKs for each user’s device configuration, so users download only the code and resources they need to run your app.&lt;br&gt;
 _ &lt;a href="https://developer.android.com/guide/playcore/feature-delivery" rel="noopener noreferrer"&gt;Android Documentation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2800%2F0%2AFitxQQeB7XC7MVUq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2800%2F0%2AFitxQQeB7XC7MVUq.png" alt="Image by [miro.medium.com](https://miro.medium.com/max/1400/1*rD_O9Ru8WJJMm1TeIy-6lw.png)" width="800" height="353"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adaptive layouts
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AMHJwbEuvl8cXDjeq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AMHJwbEuvl8cXDjeq.png" alt="Image by [android-developers.googleblog.com](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg5R2hd0VZp__xKUUgs6-tfKHEoabMPqo2aY6uoGvzre-9E4gUlz6RbGsrE-Txszbrc3OaNL9r2TshsZmzGhEiM3M-_eO8M39K6ljm9NrX2BMHRLHM3HeF04YgJf8l4Z1-kNaP9YV8BCRe3n2zTUTSx3FOvA5IRc4PCjVPiJ7CEw7M7Y7uAJLVd7WQ/s1600/Android-GoogleIO3thingstoknowaboutFormFactors_4209x1253.png)" width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the growth in the use of mobile devices with different form factors, we need to have tools that allow us to work with our Android applications adapted to different types of screens. That is why Android provides us with &lt;strong&gt;Window Size Classes&lt;/strong&gt;, which, in a simple way, are three large groups of screen formats that mark critical points for us to develop our designs. With this we avoid the complexity of thinking about many screen designs to reduce our possibilities to 3 groups which are: &lt;strong&gt;Compat&lt;/strong&gt;, &lt;strong&gt;Medium&lt;/strong&gt; and &lt;strong&gt;Expanded.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Window Size Classes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3820%2F1%2A5Tm17OKlC5n0oy6L641A5g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3820%2F1%2A5Tm17OKlC5n0oy6L641A5g.png" alt="Image by [developer.android.com](https://developer.android.com/static/images/guide/topics/large-screens/window_size_classes_width.png)" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3784%2F1%2AQv1nt0JJzQPzFfr2G78ulg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3784%2F1%2AQv1nt0JJzQPzFfr2G78ulg.png" alt="Image by [developer.android.com](https://developer.android.com/static/images/guide/topics/large-screens/window_size_classes_height.png)" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.android.com/guide/topics/large-screens/support-different-screen-sizes" rel="noopener noreferrer"&gt;Support different screen sizes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another important resource that we have are the &lt;a href="https://m3.material.io/foundations/adaptive-design/canonical-layouts" rel="noopener noreferrer"&gt;&lt;strong&gt;Canonical Layouts&lt;/strong&gt;&lt;/a&gt;, which are predefined screen designs that can be used for most scenarios in our Android applications and also show us a guide on how to adapt them to large screens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F05zdh07kg07m1670hosy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F05zdh07kg07m1670hosy.gif" alt="Window Size Classes" width="600" height="360"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Other related resources&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://android-developers.googleblog.com/2022/05/form-factors-google-io-22.html" rel="noopener noreferrer"&gt;3 things to know about Form Factors at Google I/O 2022&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/playlist?list=PLWz5rJ2EKKc_seN7mrwgU7mDYKA0hq_ib" rel="noopener noreferrer"&gt;Playlist: Form Factors at Google I/O&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://developer.android.com/courses/pathways/jetpack-compose-for-android-developers-5" rel="noopener noreferrer"&gt;Form-Factor Training&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/playlist?list=PLWz5rJ2EKKc_seN7mrwgU7mDYKA0hq_ib" rel="noopener noreferrer"&gt;Form Factors at Google I/O 2022&lt;/a&gt; (2)&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F4096%2F0%2AQcvMmljmmcvCuqfN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F4096%2F0%2AQcvMmljmmcvCuqfN.png" alt="Image by [android-developers.googleblog.com](https://android-developers.googleblog.com/2021/09/app-performance-to-drive-app-excellence.html)" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While we develop applications for Android, we must ensure that the user experience is better, not only at the beginning of the application but also throughout its execution. For this reason, it is important to have tools that allow us to carry out a preventive analysis and constant monitoring of cases that may affect the performance of the application, so here is a list of tools that will help you with this purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/topic/performance/benchmarking/benchmarking-overview" rel="noopener noreferrer"&gt;Benchmark&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/topic/performance/baselineprofiles/overview" rel="noopener noreferrer"&gt;Baseline Profiles&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/topic/libraries/app-startup" rel="noopener noreferrer"&gt;App Startup&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://firebase.google.com/docs/perf-mon" rel="noopener noreferrer"&gt;Firebase Performance Monitoring&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.android.com/topic/performance/jankstats" rel="noopener noreferrer"&gt;JankStats library&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;In-App Updates&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;When your users keep your app up to date on their devices, they can try new features, as well as benefit from performance improvements and bug fixes. Although some users enable background updates when their device is connected to an unmetered connection, other users might need to be reminded to install updates. In-app updates is a Google Play Core libraries feature that prompts active users to update your app.&lt;br&gt;
 The in-app updates feature is supported on devices running Android 5.0 (API level 21) or higher. Additionally, in-app updates are only supported for Android mobile devices, Android tablets, and Chrome OS devices.&lt;br&gt;
 _ &lt;a href="https://developer.android.com/guide/playcore/in-app-updates" rel="noopener noreferrer"&gt;In-App Updates documentation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2Am8wEQzEW1M1fwwKC.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2Am8wEQzEW1M1fwwKC.png" alt="Image by [developer.android.com](https://developer.android.com/static/images/app-bundle/flexible_flow.png)" width="800" height="527"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  In-App Reviews
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The Google Play In-App Review API lets you prompt users to submit Play Store ratings and reviews without the inconvenience of leaving your app or game.&lt;br&gt;
 Generally, the in-app review flow can be triggered at any time throughout the user journey of your app. During the flow, the user has the ability to rate your app using the 1 to 5 star system and to add an optional comment. Once submitted, the review is sent to the Play Store and eventually displayed.&lt;br&gt;
 To protect user privacy and avoid API misuse, there are strict guidelines that your app should follow about &lt;a href="https://developer.android.com/guide/playcore/in-app-review#when-to-request" rel="noopener noreferrer"&gt;when to request in-app reviews&lt;/a&gt; and the &lt;a href="https://developer.android.com/guide/playcore/in-app-review#design-guidelines" rel="noopener noreferrer"&gt;design of the review prompt&lt;/a&gt;.&lt;br&gt;
 _ &lt;a href="https://developer.android.com/guide/playcore/in-app-review" rel="noopener noreferrer"&gt;In-App Reviews documentation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fra6rwxmqpk5cp3dn9cxy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fra6rwxmqpk5cp3dn9cxy.jpg" alt="Cover image for In-App Review" width="800" height="385"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AfO3BnqLh8b-H_zLo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AfO3BnqLh8b-H_zLo.png" alt="Image by [fscl01.fonpit.de](https://fscl01.fonpit.de/userfiles/7613938/image/AccessibilityAndroid-w1400h788.png)" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Accessibility is an important feature in the design and construction of software that provides the ability for people with accessibility needs to use the application, in addition to improving their user experience. Some disabilities that this concept aims to improve are: people with vision problems, color blindness, hearing problems, dexterity problems, and cognitive disabilities, among others.&lt;/p&gt;

&lt;p&gt;Considerations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Increase text visibility (Color contrast, Resizable Text)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use large, simple controls&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Describe each UI element&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check &lt;a href="https://developer.android.com/guide/topics/ui/accessibility" rel="noopener noreferrer"&gt;Accessibility — Android doc.&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2068%2F0%2AFk42FqLrujNE0O1Z" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2068%2F0%2AFk42FqLrujNE0O1Z" alt="Image by [android.com](https://lh3.googleusercontent.com/zSpjW3Lox9DytJ8nrmuWEZgsbmFlkBkemjZc_60vWk3QCN1jTkp5SQk78qfr7TTAM5BsG4HEvmWDnzA6xO6d4srbpTUKCD40eUlO=w1034-rw-e365-v1)" width="1034" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Security is one, if not the most important aspect, that we must take into account when developing applications that protect the integrity of the device, the security of the data, and the trust of the user, which is why I list below a series of tips that will help you with this purpose.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Encrypt sensitive data and files: Use &lt;a href="https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences" rel="noopener noreferrer"&gt;EncryptedSharedPreferences&lt;/a&gt; and &lt;a href="https://developer.android.com/reference/androidx/security/crypto/EncryptedFile" rel="noopener noreferrer"&gt;EncryptedFile&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apply signature-based permissions:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;strong&gt;signature-based permissions&lt;/strong&gt; when sharing data between apps you have control over.&lt;/p&gt;

&lt;p&gt;
    package="com.example.myapp"&amp;gt;&lt;br&gt;
    
                android:protectionLevel="signature" /&amp;gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do not put keys, tokens, or sensitive data required for your application’s configuration directly inside files or classes that are inside the project repository. Use &lt;strong&gt;local.properties&lt;/strong&gt; instead.&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Version Catalogs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" alt="Cover image for Version Catalogs" width="800" height="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gradle provides a standard way to centrally manage project dependencies called the version catalog; it was experimentally introduced in version 7.0 and officially released in version 7.4.&lt;/p&gt;

&lt;p&gt;Advantage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;For each catalog, Gradle generates &lt;em&gt;type-safe accessors&lt;/em&gt; so that you can easily add dependencies with autocompletion in the IDE.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each catalog is visible to all projects of a build. It is a central place to declare a version of a dependency and to make sure that a change to that version applies to every subproject.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Catalogs can declare &lt;a href="https://docs.gradle.org/current/userguide/platforms.html#sec:dependency-bundles" rel="noopener noreferrer"&gt;dependency bundles&lt;/a&gt;, which are “groups of dependencies” that are commonly used together.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Catalogs can separate the group and name of a dependency from its actual version and use &lt;a href="https://docs.gradle.org/current/userguide/platforms.html#sec:common-version-numbers" rel="noopener noreferrer"&gt;version references&lt;/a&gt; instead, making it possible to share a version declaration between multiple dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.gradle.org/current/userguide/platforms.html" rel="noopener noreferrer"&gt;see more&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Logger
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud5zkoochkfu3n03v2qh.png" alt="Cover image for Logger" width="800" height="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A logger is a software tool used to register information about the execution of a program; important events, errors debug messages and other information that may be useful in diagnosing problems or understanding how a program is working. Loggers can be configured to write messages to different locations, such as a log file, to the console, to a database, or by sending the messages to a logging server.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/JakeWharton/timber" rel="noopener noreferrer"&gt;Timber&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv308f71t2v2or43s7f9x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv308f71t2v2or43s7f9x.png" alt="Cover image for Logger" width="800" height="4"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Linter
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2800%2F0%2AT3lk9cUYryUAo6G1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2800%2F0%2AT3lk9cUYryUAo6G1.png" alt="Image taken from [https://miro.medium.com/](https://miro.medium.com/max/1400/1*gdHPyd_S5UgCiFKnetAHvA.webp)" width="800" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Linter is a programming tool that is used to analyze the program source code to find potential problems or bugs in the code. These issues can be syntactic, inappropriate code style, lack of documentation, security issues, and so on, and they can have an impact on the quality and maintainability of the code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/swlh/what-is-android-lint-17fa0d87abb2" rel="noopener noreferrer"&gt;Android Lint&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://detekt.dev/" rel="noopener noreferrer"&gt;Detekt&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pinterest.github.io/ktlint/" rel="noopener noreferrer"&gt;Ktlint&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj39v0shfjc2gmtxaew0j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj39v0shfjc2gmtxaew0j.png" alt="Divider" width="800" height="4"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;If you like my content and want to support my work, you can give me a cup of coffee ☕️ 🥰&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/devjorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj18vjijbegi1dhp83ozv.png" alt="Ko-fi" width="300" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/jorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjdz714gpdbxv6ve001hf.png" alt="Buy me a coffee" width="299" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow me in
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Twitter:&lt;/strong&gt; &lt;a href="https://twitter.com/devjcastro" rel="noopener noreferrer"&gt;@devjcastro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Linkedin: &lt;a href="https://www.linkedin.com/in/dev-jorgecastro/" rel="noopener noreferrer"&gt;dev-jorgecastro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>leadership</category>
      <category>career</category>
      <category>workplace</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Add blur effect to Image in Jetpack Compose</title>
      <dc:creator>Jorge Castro</dc:creator>
      <pubDate>Mon, 09 Jan 2023 01:43:49 +0000</pubDate>
      <link>https://dev.to/jorgecastro/add-blur-effect-to-image-in-jetpack-compose-bl8</link>
      <guid>https://dev.to/jorgecastro/add-blur-effect-to-image-in-jetpack-compose-bl8</guid>
      <description>&lt;p&gt;Hi guys, today I want to share this little article where I briefly explain how to create a blurry image on Android. Without so much introduction, let’s get to the point. 😎&lt;/p&gt;

&lt;p&gt;Blur is a visual effect that is often used in graphics software to reduce the clarity or sharpness of an image or video. It can be used for a variety of purposes, such as to reduce distractions in the background of a photo or to create a more visually pleasing composition.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsyfb4rvd9o3za4xmvpgu.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsyfb4rvd9o3za4xmvpgu.jpeg" alt="Photo by gebhartyler on Unsplash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/es/fotos/AQpOwYbm_Jg" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As developers, at some point we may have a need to create blurry images, so today I want to share with you a solution that is backward compatible with Android API 31.&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;When we work with Jetpack Compose, we can apply a blur effect in a very simple way using the blur extension function of the Modifier interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fredngq5uyew8qoqn9kaq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fredngq5uyew8qoqn9kaq.png" alt="Example of how to create an image in Jetpack Compose"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem with the example above is that if it runs on versions below Android 12, we will not see any effect because this implementation is only allowed in Android 12 (API level 31) or higher versions.&lt;/p&gt;

&lt;p&gt;So we will have to verify if we are running the application on versions equal to or greater than Android 12.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fybgc8c97n9q79917yz1n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fybgc8c97n9q79917yz1n.png" alt="Image that shows how the Android version is verified"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first thing we will do is create our code for versions prior to Android 12. For this, we will use RenderScript and ScriptIntrinsicBlur. We will create a composable function called “LegacyBlurImage” that will give us an Image product of a previously processed bitmap.&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;@Composable&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;LegacyBlurImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bitmap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;blurRadio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxSize&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;renderScript&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RenderScript&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;LocalContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;bitmapAlloc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Allocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFromBitmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;renderScript&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nc"&gt;ScriptIntrinsicBlur&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="n"&gt;renderScript&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bitmapAlloc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setRadius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blurRadio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;setInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitmapAlloc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitmapAlloc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;bitmapAlloc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;renderScript&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="nc"&gt;BlurImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modifier&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;We will also create a composable function that we will call “BlurImage” with which we will create the composable for the image we want to display.&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;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;BlurImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bitmap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxSize&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="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;bitmap&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asImageBitmap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="n"&gt;contentDescription&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;contentScale&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ContentScale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Crop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modifier&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;Finally we call our functions checking the version of Android that the device has.&lt;/p&gt;

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

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;bitmap&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BitmapFactory&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decodeResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LocalContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drawable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom_image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;VERSION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SDK_INT&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nc"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;VERSION_CODES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;S&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;LegacyBlurImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;25f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;BlurImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;Modifier&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxSize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blur&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;radiusX&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;radiusY&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;Result&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff68uhg0x1edut1j89nd2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff68uhg0x1edut1j89nd2.png" alt="Final image result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see the complete code &lt;a href="https://gist.github.com/devjorgecastro/f2289544d40cbf70fcab1df05d2e0b06" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;If you like my content and want to support my work, you can give me a cup of coffee ☕️ 🥰&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/devjorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj18vjijbegi1dhp83ozv.png" alt="Ko-fi"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/jorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjdz714gpdbxv6ve001hf.png" alt="Buy me a coffee"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow me in
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Twitter:&lt;/strong&gt; &lt;a href="https://twitter.com/devjcastro" rel="noopener noreferrer"&gt;@devjcastro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Linkedin:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/dev-jorgecastro/" rel="noopener noreferrer"&gt;dev-jorgecastro&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>android</category>
      <category>jetpackcompose</category>
      <category>kotlin</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Generic MongoDB function in Rust</title>
      <dc:creator>Jorge Castro</dc:creator>
      <pubDate>Sun, 13 Nov 2022 08:46:18 +0000</pubDate>
      <link>https://dev.to/jorgecastro/generic-mongodb-function-in-rust-8df</link>
      <guid>https://dev.to/jorgecastro/generic-mongodb-function-in-rust-8df</guid>
      <description>&lt;p&gt;Hello guys!. As an Android Developer 🤖❤️, I find myself learning Rust and it seems more and more entertaining to play in my spare time with this language. Today I want to write a bit and this time we will create a small utility to work with MongoDB cursors in Rust in a generic way.&lt;/p&gt;

&lt;p&gt;While having fun with Rust and doing a couple of queries with MongoDB I realized that I was repeating the same pattern in different places: a listing of N things that follow an identical query structure in MongoDB. Giving a little more context, when we want to retrieve a set of data from a Mongo collection, we get a cursor of the type of data we want. Based on that scenario, I wanted to create a small function that would help me convert that cursor to an array of data of type T, where T is a generic type; that is, when calling my function T, it will take the specific type that it needs at that moment.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Generic Data Types
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;We use generics to create definitions for items like function signatures or structs, which we can then use with many different concrete data types.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;you can see more about the generic data types &lt;a href="https://doc.rust-lang.org/book/ch10-01-syntax.html"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Let’s get to the point
&lt;/h3&gt;

&lt;p&gt;First of all, I want to start from the fact that it is not a tutorial that starts from scratch, I am assuming that you know and understand the most basic concepts of Rust such as: how to create a project, functions and modules in Rust, among other small concepts . Having said all of the above, let’s continue with the article.&lt;/p&gt;

&lt;p&gt;We’ll start by defining our generic function that will receive a generic cursor &lt;code&gt;Cursor::&amp;lt;T&amp;gt;&lt;/code&gt; and return a data set &lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;/code&gt;. To define our generic function we will create a module mod.rs (You name it to your liking). For this example we will call our function &lt;code&gt;extract_data&lt;/code&gt; and add the code below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;mongodb&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cursor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;futures&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;StreamExt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rocket&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;serde&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DeserializeOwned&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;extract_data&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DeserializeOwned&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Unpin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Cursor&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s create a small function to get a list of users. Don’t focus on the content of the function; surely you can make better code. We will call this function &lt;code&gt;get_users_cursor&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_users_cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Cursor&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;client_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;ClientOptions&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DATABASE_URI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;client_options&lt;/span&gt;&lt;span class="py"&gt;.app_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;APP_NAME&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DATABASE_NAME&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DATABASE_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="py"&gt;.collection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="nf"&gt;.find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&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;Finally, it’s time to call our generic function to obtain a dataset of the generic type, which in our example is &lt;code&gt;UserModel&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fetch_detail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.get_users_cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nn"&gt;extract_data&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember to import the module path where you defined the &lt;code&gt;extract_data&lt;/code&gt; generic function.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;If you like my content and want to support my work, you can give me a cup of coffee ☕️ 🥰&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/devjorgecastro"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vcf9aDqi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j18vjijbegi1dhp83ozv.png" alt="Ko-fi" width="300" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/jorgecastro"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D2yC8lNE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jdz714gpdbxv6ve001hf.png" alt="Buy me a coffee" width="299" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Follow me in
&lt;/h3&gt;

&lt;p&gt;Twitter: &lt;a href="https://twitter.com/devjcastro"&gt;@devjcastro&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>programming</category>
      <category>development</category>
      <category>backend</category>
    </item>
    <item>
      <title>Live Reloading in Rust with Cargo Watch and Docker</title>
      <dc:creator>Jorge Castro</dc:creator>
      <pubDate>Tue, 18 Oct 2022 16:19:46 +0000</pubDate>
      <link>https://dev.to/jorgecastro/hot-reload-in-rust-with-cargo-watch-and-docker-5d25</link>
      <guid>https://dev.to/jorgecastro/hot-reload-in-rust-with-cargo-watch-and-docker-5d25</guid>
      <description>&lt;p&gt;Helo everyone 👋🏻&lt;br&gt;
Today I want to share with you how we can observe changes in Rust using Docker Containers. This is a continuation of the article &lt;a href="https://dev.to/devjorgecastro/hot-reload-in-rust-with-cargo-watch-5bon"&gt;Hot Reload in Rust with Cargo Watch&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;1 - First and foremost, we must create a DockerFile in the project's root directory and include the code below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Using official rust base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; rust:alpine3.16&lt;/span&gt;

&lt;span class="c"&gt;# Set the application directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Install musl-tools to make many crates compile successfully&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; musl-dev

&lt;span class="c"&gt;# Install cargo-watch&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;cargo &lt;span class="nb"&gt;install &lt;/span&gt;cargo-watch

&lt;span class="c"&gt;# Copy the files to the Docker image&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./ ./&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Dockerfile is a plain text file that contains instructions that Docker will use to build an image. &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fdocs.docker.com%2Fengine%2Freference%2Fbuilder%2F" rel="noopener noreferrer"&gt;see more&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
2 - &lt;del&gt;The following step is to create the image using the command below&lt;/del&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker build -t rust-observable-image .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should then see on your terminal the response confirming that the image has been successfully built.&lt;br&gt;
&lt;code&gt;[+] Building 430.0s (10/10) FINISHED&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fduknbkowoszswfhwm8c1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fduknbkowoszswfhwm8c1.png" alt="Building result of Docker Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can verify that the image is created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2305jmy072ojxtbevbe9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2305jmy072ojxtbevbe9.png" alt="List Docker images command"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3 - The next step would be to run the container and run the "cargo watch" command inside the container, but we're going to do something more automated using Docker Compose. So we don't need to run step 2 because when we start the container with Docker Compose it will run automatically.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
So we need to create a docker-compose.yml file inside the root directory of the project and add the following instructions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.9"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
        &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cargo-watch-example"&lt;/span&gt;
        &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.:/app&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sh -c "cargo watch -x run"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4 - The final step is run the container with Docker Compose&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8690zq5kk3m3k4zkxmeg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8690zq5kk3m3k4zkxmeg.png" alt="docker compose up"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From now on you will be able to make changes to your project and observe the changes from your container.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;If you like my content and want to support my work, you can give me a cup of coffee ☕️ 🥰&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/devjorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj18vjijbegi1dhp83ozv.png" alt="Ko-fi"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/jorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjdz714gpdbxv6ve001hf.png" alt="Buy me a coffee"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Follow me in
&lt;/h3&gt;

&lt;p&gt;Twitter: &lt;a href="https://twitter.com/devjcastro" rel="noopener noreferrer"&gt;@devjcastro&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>development</category>
    </item>
    <item>
      <title>Live Reloading in Rust with Cargo Watch</title>
      <dc:creator>Jorge Castro</dc:creator>
      <pubDate>Sun, 16 Oct 2022 21:47:03 +0000</pubDate>
      <link>https://dev.to/jorgecastro/hot-reload-in-rust-with-cargo-watch-5bon</link>
      <guid>https://dev.to/jorgecastro/hot-reload-in-rust-with-cargo-watch-5bon</guid>
      <description>&lt;p&gt;Helo everyone 👋🏻&lt;br&gt;
When we are developing a Rust application, sometimes we have the need to reduce the time of the cycle of changes, compilation, and execution. It sounds a bit complicated, but today I am going to show you a tool that does it in a very simple and automatic way. This tool is called &lt;code&gt;cargo watch&lt;/code&gt;, and it reduces the time of each cycle of changes in the background.&lt;/p&gt;
&lt;h4&gt;
  
  
  Cargo Watch
&lt;/h4&gt;

&lt;p&gt;Cargo Watch creates a listener on your project changes and runs Cargo commands when they occur. At the time of writing this article the latest version is &lt;code&gt;8.1.2&lt;/code&gt;.&lt;br&gt;
&lt;a href="https://docs.rs/crate/cargo-watch/8.1.2" rel="noopener noreferrer"&gt;Cargo Watch doc&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/watchexec/cargo-watch/releases" rel="noopener noreferrer"&gt;Github Project&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Disclaimer
&lt;/h3&gt;

&lt;p&gt;For this tutorial I am assuming you are familiar with using &lt;strong&gt;Cargo&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Cargo is Rust’s build system and package manager. Most Rustaceans use this tool to manage their Rust projects because Cargo handles a lot of tasks for you, such as building your code, downloading the libraries your code depends on, and building those libraries&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can read more about cargo &lt;a href="https://doc.rust-lang.org/book/ch01-03-hello-cargo.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Change Cycle
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fql4uijp53o8vnb35kagh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fql4uijp53o8vnb35kagh.png" alt="Change Cycle" width="800" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We start by creating a new project. For our example we will call it &lt;code&gt;cargo-watch-example&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cargo new cargo-watch-example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the project was created correctly you should see the following message in the terminal&lt;br&gt;
&lt;code&gt;Created binary (application)&lt;/code&gt;cargo-watch-example&lt;code&gt;package&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Install Cargo Watch
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cargo install cargo-watch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Run project and observe changes
&lt;/h3&gt;

&lt;p&gt;Inside the root directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cargo watch -x run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to only listen for changes from only the working directory add the -w option to specify a file or directory from which you want to listen for changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cargo watch -w src -x run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Demo
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8kzyp4qqebx3d38b2dd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8kzyp4qqebx3d38b2dd.gif" alt="Cargo Watch Demo" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The previous example was the simplest configuration observing changes on the whole project or on a specific directory, but you can do much more, Here's a copy of the help:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;USAGE:
    cargo watch [FLAGS] [OPTIONS]

FLAGS:
    -c, --clear              Clear the screen before each run
    -h, --help               Display this message
        --ignore-nothing     Ignore nothing, not even target/ and .git/
        --debug              Show debug output
        --why                Show paths that changed
    -q, --quiet              Suppress output from cargo-watch itself
        --no-gitignore       Don’t use .gitignore files
        --no-ignore          Don’t use .ignore files
        --no-restart         Don’t restart command while it’s still running
        --poll               Force use of polling for file changes
        --postpone           Postpone first run until a file changes
    -V, --version            Display version information
        --watch-when-idle    Ignore events emitted while the commands run.
                             Will become default behaviour in 8.0.

OPTIONS:
    -x, --exec &amp;lt;cmd&amp;gt;...
            Cargo command(s) to execute on changes [default: check]

    -s, --shell &amp;lt;cmd&amp;gt;...           Shell command(s) to execute on changes

    -d, --delay &amp;lt;delay&amp;gt;
            File updates debounce delay in seconds [default: 0.5]

        --features &amp;lt;features&amp;gt;
            List of features passed to cargo invocations

    -i, --ignore &amp;lt;pattern&amp;gt;...      Ignore a glob/gitignore-style pattern

    -B &amp;lt;rust-backtrace&amp;gt;
            Inject RUST_BACKTRACE=VALUE (generally you want to set it to 1)
            into the environment

        --use-shell &amp;lt;use-shell&amp;gt;
            Use a different shell. E.g. --use-shell=bash. On Windows, try
            --use-shell=powershell, which will become the default in 8.0.

    -w, --watch &amp;lt;watch&amp;gt;...
            Watch specific file(s) or folder(s) [default: .]

    -C, --workdir &amp;lt;workdir&amp;gt;
            Change working directory before running command [default: crate root]

ARGS:
    &amp;lt;cmd:trail&amp;gt;...    Full command to run. -x and -s will be ignored!

Cargo commands (-x) are always executed before shell commands (-s). You can use
the `-- command` style instead, note you'll need to use full commands, it won't
prefix `cargo` for you.

By default, your entire project is watched, except for the target/ and .git/
folders, and your .ignore and .gitignore files are used to filter paths.

On Windows, patterns given to -i have forward slashes (/) automatically
converted to backward ones (\) to ease command portability.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the next tutorial we will apply this example using &lt;code&gt;Docker Containers&lt;/code&gt; 😎&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;If you like my content and want to support my work, you can give me a cup of coffee ☕️ 🥰&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/devjorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj18vjijbegi1dhp83ozv.png" alt="Ko-fi" width="300" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/jorgecastro" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjdz714gpdbxv6ve001hf.png" alt="Buy me a coffee" width="299" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Follow me in
&lt;/h3&gt;

&lt;p&gt;Twitter: &lt;a href="https://twitter.com/devjcastro" rel="noopener noreferrer"&gt;@devjcastro&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>development</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Strategies to have fun while learning technology</title>
      <dc:creator>Jorge Castro</dc:creator>
      <pubDate>Sun, 09 Oct 2022 15:10:02 +0000</pubDate>
      <link>https://dev.to/jorgecastro/how-to-learn-technology-in-a-fun-way-504b</link>
      <guid>https://dev.to/jorgecastro/how-to-learn-technology-in-a-fun-way-504b</guid>
      <description>&lt;p&gt;First of all, I want to say that this article is more focused on technology topics, but it could be applied to other &lt;code&gt;contexts&lt;/code&gt;. It is just a personal opinion from the point of view of a technology lover ❤️ and, in particular, a Software Engineer 💻.&lt;/p&gt;

&lt;p&gt;Some people ask me what is my strategy to keep learning all the time? well, today I want to share a little about this.&lt;/p&gt;

&lt;p&gt;It is very normal that when we need to learn a new topic, the first thing we think is to go to the &lt;code&gt;official documentation&lt;/code&gt;, check the &lt;a href="https://stackoverflow.com/"&gt;Stackoverflow&lt;/a&gt; page and watch some videos. But sometimes this could just be a bit boring, so here are the most common things I do when I want to stay focused and learn a new topic in a fun way.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Follow people with common interests.&lt;/li&gt;
&lt;li&gt;Participate in a technology community.&lt;/li&gt;
&lt;li&gt;Discuss.&lt;/li&gt;
&lt;li&gt;Create a test project.&lt;/li&gt;
&lt;li&gt;Write an Article.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are other ways, but these are the ones I usually use the most, and we don't want to extend this article much, right? 😅.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow people with common interests. 👤
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JNQLzqz1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lfjvgnwzzozmiwzdxhs6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JNQLzqz1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lfjvgnwzzozmiwzdxhs6.jpeg" alt="Follow people with common interests" width="880" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/es/@freewalkingtoursalzburg?utm_source=medium&amp;amp;utm_medium=referral"&gt;Free Walking Tour Salzburg&lt;/a&gt; on &lt;a href="https://unsplash.com/es?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To keep myself updated and, as a user of different platforms or social networks, I usually follow the content and publications of different people who take the time to share valuable and relevant content of my interest. Some of my favorite content platforms are: &lt;a href="https://github.com/"&gt;Github&lt;/a&gt;, &lt;a href="https://linkedin.com/"&gt;Linkedin&lt;/a&gt;, &lt;a href="https://medium.com/"&gt;Medium&lt;/a&gt;, &lt;a href="https://dev.to/"&gt;Dev community&lt;/a&gt; and &lt;a href="https://twitter.com"&gt;Twitter&lt;/a&gt;. In this way, I can see and compare what I am learning with what other people have already learned and who surely have a higher technical level.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Participate in a technology community. 👥
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qShfZL8t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3zfv67m0wst06g9ze1v9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qShfZL8t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3zfv67m0wst06g9ze1v9.jpeg" alt="Cover Image for tech community" width="880" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/es/@windows?utm_source=medium&amp;amp;utm_medium=referral"&gt;Windows&lt;/a&gt; on &lt;a href="https://unsplash.com/es?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This section is one of my favorites because I like to meet people, get to know different cultures, listen to different opinions, share a bit of my professional experience, and try to help solve doubts and problems that other members have.&lt;/p&gt;

&lt;p&gt;I really enjoy being part of tech communities and helping them grow by sharing ideas and topics that I’m learning about that I find interesting and useful to other members of the community.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Discuss. 💬
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6q9I9_TE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i9o0v1t6xnm8188koxqm.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6q9I9_TE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i9o0v1t6xnm8188koxqm.jpeg" alt="Photo taken from Pexels" width="880" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The debate with other people who share similar knowledge is another interesting way to validate what has been learned. This helps to correct ambiguities and, of course, generate new ideas from different points of view. On the other hand, it helps to improve assertive communication, which is essential for our personal and professional growth.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a test project 💻
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World!"&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;Obviously, for most cases, the above example is too basic, but the main idea is to put into practice the acquired knowledge. Many people say that if it is not put into practice, then it is forgotten, and I believe it 🙂&lt;/p&gt;

&lt;p&gt;When I start to learn a new technology, I do a small project to apply the acquired knowledge. It's great fun for me. I feel that it's a hobby and I can spend hours having fun while I learn. I also re-practice previously acquired knowledge 🤓.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Write an article. 📖
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Yec2nbZv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ai83cpsfx7f0sj4icc6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Yec2nbZv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ai83cpsfx7f0sj4icc6.jpeg" alt="Photo taken from Pexels" width="880" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wait, wait! Write? We are supposed to want to learn. Well, I think that one of the best ways to keep knowledge active is through writing. It's a fun way to post what we've learned. Honestly, to date, it is a new path that I am putting into practice, and that is why I have decided to write articles about the things that I am learning in the world of technology. The main purpose of this is to spare other people certain complexities that I have gone through in learning certain topics. On the other hand, when we write, we can also create our own record of knowledge.&lt;/p&gt;

&lt;p&gt;At the end of the day, there are many ways to maintain your focus while improving your learning experience. The important thing is to apply a strategy to your personal taste that helps you achieve your goal in the most fun way possible. It is also important that you keep in mind that discipline and perseverance are essential for learning to become effective.&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;If you like my content and want to support my work, you can give me a cup of coffee ☕️ 🥰&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/devjorgecastro"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vcf9aDqi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j18vjijbegi1dhp83ozv.png" alt="Ko-fi" width="300" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/jorgecastro"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D2yC8lNE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jdz714gpdbxv6ve001hf.png" alt="Buy me a coffee" width="299" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Follow me in
&lt;/h3&gt;

&lt;p&gt;Twitter: &lt;a href="https://twitter.com/devjcastro"&gt;@devjcastro&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
