<?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: Anes Abismail</title>
    <description>The latest articles on DEV Community by Anes Abismail (@anesabml).</description>
    <link>https://dev.to/anesabml</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%2F149597%2Fa9c338d1-734f-4ff5-ab67-078f1aba5bb8.jpeg</url>
      <title>DEV Community: Anes Abismail</title>
      <link>https://dev.to/anesabml</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anesabml"/>
    <language>en</language>
    <item>
      <title>Autostart scrcpy on device connection.</title>
      <dc:creator>Anes Abismail</dc:creator>
      <pubDate>Sun, 16 Aug 2020 13:03:40 +0000</pubDate>
      <link>https://dev.to/anesabml/autostart-scrcpy-on-device-connection-39a</link>
      <guid>https://dev.to/anesabml/autostart-scrcpy-on-device-connection-39a</guid>
      <description>&lt;h1&gt;
  
  
  OnCreateBlog:
&lt;/h1&gt;

&lt;p&gt;When it comes to programming and setting up my workflow, I always try to find a solution to automate basic tasks and focus on what matters, also it gives me a sense of magic. One of the things that were bugging me lately is manually launching &lt;a href="https://github.com/Genymobile/scrcpy"&gt;scrcpy&lt;/a&gt; every time I plug my phone to test my apps, in this blog post I will show you how I automated that process, note that this setup is for macOS.&lt;/p&gt;

&lt;p&gt;There's a command-line tool allows to execute a command whenever a new device is connected to adb, but that didn't work for me, you can check out &lt;a href="https://github.com/rom1v/autoadb"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  My thought process:
&lt;/h1&gt;

&lt;p&gt;As for each problem a plan is required, my initial thought was to set up some kind of Cron job that runs every second and check for new USB devices, but this is brute force and it's not efficient to run a job every second, even if we set the job to run every minute that would not be a good idea either if I plug my phone I might have to wait one minute for the job to launch &lt;a href="https://github.com/Genymobile/scrcpy"&gt;scrcpy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Okay what other options we have, let's run a job and write an infinite loop that checks for new USB devices. this solves the instant problem but it's not efficient.&lt;/p&gt;

&lt;p&gt;I had one last thought and it's to set up some sort of a listener, callback on an event that gets triggered every time a USB device is plugged in. After doing some research I found out that it is possible thanks to &lt;code&gt;launchd&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What is launchd:
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;launchd&lt;/code&gt; is a service management tool used by macOS it was created by Apple, it's similar in some ways to &lt;code&gt;systemd&lt;/code&gt; on Linux.&lt;/p&gt;

&lt;p&gt;Another definition according to Apple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The launchd process is used by macOS to manage daemons and agents, and you can use it to run your shell scripts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But wait what are daemons and agents?&lt;/p&gt;

&lt;p&gt;A Daemon is a computer program that runs on background without any direct control and interaction with the user, they are usually launched when the system boots. An example of a daemon is a backup schedule when you enable Time Machine.&lt;/p&gt;

&lt;p&gt;An Agent is very familiar to a Daemon but it runs within the user session, which means that they are launched when the user logs in.&lt;/p&gt;

&lt;p&gt;Based on the &lt;a href="https://support.apple.com/en-gb/guide/terminal/apdc6c1077b-5d5d-4d35-9c19-60f2397b2369/mac"&gt;Apple documentation&lt;/a&gt; we can find the various daemons and agents managed by &lt;code&gt;launchd&lt;/code&gt; by looking inside the following folders:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tables&lt;/th&gt;
&lt;th&gt;Are&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;/System/Library/LaunchDaemons&lt;/td&gt;
&lt;td&gt;Apple-supplied system daemons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/System/Library/LaunchAgents&lt;/td&gt;
&lt;td&gt;Apple-supplied agents that apply to all users on a per-user basis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/Library/LaunchDaemons&lt;/td&gt;
&lt;td&gt;Third-party system daemons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/Library/LaunchAgents&lt;/td&gt;
&lt;td&gt;Third-party agents that apply to all users on a per-user basis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~/Library/LaunchAgents&lt;/td&gt;
&lt;td&gt;Third-party agents that apply only to the logged-in user&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Creating a LaunchDaemon or LaunchAgent:
&lt;/h1&gt;

&lt;p&gt;Based on those definitions, we will proceed with creating an Agent because it's more suitable to solve our problem, we don't want to launch &lt;a href="https://github.com/Genymobile/scrcpy"&gt;scrcpy&lt;/a&gt; if the user is not logged in.&lt;/p&gt;

&lt;p&gt;To create a new &lt;code&gt;LaunchAgent&lt;/code&gt; we need to add our config file to the respective directory, I will be adding my config file to &lt;code&gt;~/Library/LaunchAgents&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Those files require a specific format: &lt;code&gt;plist&lt;/code&gt; (property list) it holds the specific information of the task. Below are the required keys to include in the file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Label: Unique task identifier.&lt;/li&gt;
&lt;li&gt;Program or ProgramArguments: String Path to executable.&lt;/li&gt;
&lt;li&gt;RunAtLoad: (True or False) Run job immediately when &lt;code&gt;plist&lt;/code&gt; file is loaded.&lt;/li&gt;
&lt;li&gt;LaunchOnlyOnce: Run job once, and never try again.&lt;/li&gt;
&lt;li&gt;LaunchEvents: there is more than one trigger or launch event but the one that we are going to use is: &lt;code&gt;I/O kit notifications - USB device.&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that the difference between Program and ProgramArguments is that when using ProgramArguments you can specify the path to executable and an array of arguments arg[0],arg[1],....&lt;/p&gt;

&lt;p&gt;So hers is my config file, which I name &lt;code&gt;com.oneplus5t.scrcpy.plist&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN &amp;lt;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;gt;&lt;/span&gt; &amp;gt;
&lt;span class="nt"&gt;&amp;lt;plist&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;com.oneplus5t.scrcpy&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/usr/local/bin/launch_iterm.bash&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;scrcpy&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;LaunchEvents&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;com.apple.iokit.matching&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;com.apple.device-attach&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;idProduct&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;1234&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;idVendor&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;1444&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;IOProviderClass&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;IOUSBDevice&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;IOMatchLaunchStream&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To find your phone's idProduct and idVendor run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;ioreg &lt;span class="nt"&gt;-p&lt;/span&gt; IOUSB &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Explaining what the above command does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;ioreg&lt;/code&gt; displays the I/O Kit registry.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-p&lt;/code&gt; option is to traverse &lt;code&gt;IOUSB&lt;/code&gt; registry&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-l&lt;/code&gt; to show more information about the displayed objects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You may notice that we are running a script &lt;code&gt;launch_iterm.bash&lt;/code&gt;, thanks to this script we can launch &lt;a href="https://www.iterm2.com/"&gt;iTerm2&lt;/a&gt; or any other terminal application, it uses &lt;code&gt;AppleScript&lt;/code&gt; which is a scripting language created by (wait for it) → Apple. check out &lt;a href="https://gist.github.com/geyang/a495a5ea8e6fdd2f79f8e874616ea182"&gt;this&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Open new iTerm window from the command line using v3 syntax for `AppleScript` as needed in iTerm2 Version 3+&lt;/span&gt;
&lt;span class="c"&gt;# This script blocks until the cmd is executed in the new iTerm2 window.  It then leaves the window open.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# See also &amp;lt;https://www.iterm2.com/documentation-scripting.html&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Usage:&lt;/span&gt;
&lt;span class="c"&gt;#     iterm                   Opens the current directory in a new iTerm window&lt;/span&gt;
&lt;span class="c"&gt;#     iterm [PATH]            Open PATH in a new iTerm window&lt;/span&gt;
&lt;span class="c"&gt;#     iterm [CMD]             Open a new iTerm window and execute CMD&lt;/span&gt;
&lt;span class="c"&gt;#     iterm [PATH] [CMD] ...  You can probably guess&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Example:&lt;/span&gt;
&lt;span class="c"&gt;#     iterm ~/Code/HelloWorld ./setup.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# References:&lt;/span&gt;
&lt;span class="c"&gt;#     iTerm AppleScript Examples:&lt;/span&gt;
&lt;span class="c"&gt;#     &amp;lt;https://gitlab.com/gnachman/iterm2/wikis/Applescript&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Credit:&lt;/span&gt;
&lt;span class="c"&gt;#     Forked from &amp;lt;https://gist.github.com/vyder/96891b93f515cb4ac559e9132e1c9086&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#     Inspired by tab.bash by @bobthecow&lt;/span&gt;
&lt;span class="c"&gt;#     link: &amp;lt;https://gist.github.com/bobthecow/757788&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;# OSX only&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"Darwin"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'OS X Only'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;return

function &lt;/span&gt;iterm &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;wd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&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;then
        &lt;/span&gt;&lt;span class="nv"&gt;wd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
        &lt;span class="nv"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;:2&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi

    if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="s2"&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;then&lt;/span&gt;
        &lt;span class="c"&gt;# echo $args&lt;/span&gt;
        &lt;span class="nv"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi

&lt;/span&gt;osascript &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
    tell application "iTerm"
        activate
        set new_window to (create window with default profile)
        set cSession to current session of new_window
        tell new_window
            tell cSession
                delay 1
                write text "&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="sh"&gt;"
            end tell
        end tell
    end tell
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
iterm &lt;span class="nv"&gt;$@&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;One thing left to do is to load the Agent and plug your phone, to load the Agent we use the &lt;code&gt;launch&lt;/code&gt; tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;launchctl load com.oneplus5t.scrcpy.plist
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After loading the configuration file, you will notice that it's repeatedly executed each 10s, as I understand this is caused because the &lt;a href="https://github.com/Genymobile/scrcpy"&gt;&lt;code&gt;scrcpy&lt;/code&gt;&lt;/a&gt; doesn't return a result and this causes the executable being called repeatedly (if I am mistaken please correct me). Thanks to this &lt;a href="https://github.com/himbeles/mac-device-connect-daemon"&gt;repo&lt;/a&gt;, our problem can be fixed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Unload the configuration file&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;launchctl unload com.oneplus5t.scrcpy.plist
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clone the repo:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &amp;lt;https://github.com/himbeles/mac-device-connect-daemon&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Build the event stream handler:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcc &lt;span class="nt"&gt;-framework&lt;/span&gt; Foundation &lt;span class="nt"&gt;-o&lt;/span&gt; xpc_set_event_stream_handler xpc_set_event_stream_handler.m
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the result to &lt;code&gt;/usr/local/bin/&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp &lt;/span&gt;xpc_set_event_stream_handler /usr/local/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the &lt;code&gt;LaunchAgent&lt;/code&gt; configuration file:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN &amp;lt;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;gt;&lt;/span&gt; &amp;gt;
&lt;span class="nt"&gt;&amp;lt;plist&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;com.oneplus5t.scrcpy&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/usr/local/bin/xpc_set_event_stream_handler&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- Add this line --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/usr/local/bin/launch_iterm.bash&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;scrcpy&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;LaunchEvents&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;com.apple.iokit.matching&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;com.apple.device-attach&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;idProduct&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;1234&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;idVendor&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;1444&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;IOProviderClass&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;IOUSBDevice&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;IOMatchLaunchStream&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  OnDestroyBlog:
&lt;/h1&gt;

&lt;p&gt;This was a fun experiment, it opens a wide variety of automation ideas and improving my workflow, Thank you for reading I hope you learned something new and you had fun as I did, Feel free to reach out on &lt;a href="https://twitter.com/anesabml"&gt;Twitter&lt;/a&gt; if you have any questions.&lt;/p&gt;

&lt;h1&gt;
  
  
  Resources:
&lt;/h1&gt;

&lt;p&gt;&lt;a href="http://technologeeks.com/docs/launchd.pdf"&gt;Launchd - At Your Service!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.techrepublic.com/article/macos-know-the-difference-between-launch-agents-and-daemons-and-use-them-to-automate-processes/"&gt;macOS: Know the difference between launch agents and daemons, and use them to automate processes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.apple.com/library/archive/technotes/tn2083/_index.html"&gt;Daemons and Agents&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://support.apple.com/en-gb/guide/terminal/apdc6c1077b-5d5d-4d35-9c19-60f2397b2369/mac"&gt;Script management with launchd in Terminal on Mac&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/himbeles/mac-device-connect-daemon"&gt;Mac device connect daemon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/geyang/a495a5ea8e6fdd2f79f8e874616ea182"&gt;Bash function for opening up iterm form current directory&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>What I learned from 30 days of solving LeetCode problems.</title>
      <dc:creator>Anes Abismail</dc:creator>
      <pubDate>Tue, 07 Jul 2020 11:32:13 +0000</pubDate>
      <link>https://dev.to/anesabml/what-i-learned-from-30-days-of-solving-leetcode-problems-o21</link>
      <guid>https://dev.to/anesabml/what-i-learned-from-30-days-of-solving-leetcode-problems-o21</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bv3Rtwq2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1506452819137-0422416856b8%3Fixlib%3Drb-1.2.1%26ixid%3DeyJhcHBfaWQiOjEyMDd9%26auto%3Dformat%26fit%3Dcrop%26w%3D2532%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bv3Rtwq2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1506452819137-0422416856b8%3Fixlib%3Drb-1.2.1%26ixid%3DeyJhcHBfaWQiOjEyMDd9%26auto%3Dformat%26fit%3Dcrop%26w%3D2532%26q%3D80" alt="Image"&gt;&lt;/a&gt;&lt;br&gt;
&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@ikukevk?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Kevin Ku&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/coding?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Last month after I finished my Product Hunt client Android app I decided to try something new and challenge myself, so I ended up choosing to do &lt;a href="http://leetcode.com/"&gt;LeetCode&lt;/a&gt; problems, my goal was to do one problem every day no matter what, I choose to do them in the morning because I feel more energetic and it's a good way to start your day with a challenge. In this blog post, I will be sharing with you my mistakes and what I learned from this experience.&lt;/p&gt;

&lt;h1&gt;
  
  
  Consistency:
&lt;/h1&gt;

&lt;p&gt;My goal was to do one problem every day and develop a habit, at the beginning it didn't feel natural as I was trying something new and uncomfortable, but I can say that after two weeks it becomes natural every morning I pick up my laptop and notebook and start solving a problem without even noticing, it felt like a morning routine. I still missed some days but I made sure that I don't miss two days in a row.&lt;/p&gt;

&lt;h1&gt;
  
  
  Coding
&lt;/h1&gt;

&lt;p&gt;As developers, we feel that we need to start coding as fast as we can and beet our last record of typing, but if you started solving a problem with coding you will be stuck and you feel overwhelmed because you didn't understand what you are trying to solve yet. Pick up an iPad or a pen and a piper (if you are poor like me) and start writing your inputs, outputs, try to visualize the problem, write down your thought process and then try to solve it in a simple intuitive way, after that you can start coding. Here is the step that I usually take to solve a problem:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write down the input and the expected output.&lt;/li&gt;
&lt;li&gt;Understand the problem by asking yourself different questions about it.&lt;/li&gt;
&lt;li&gt;Try to identify Data Structures that might help you solve the problem.&lt;/li&gt;
&lt;li&gt;Write down the steps required to solve the problem.&lt;/li&gt;
&lt;li&gt;Write a brute force solution then try to improve it using different Data Structures and Algorithms.&lt;/li&gt;
&lt;li&gt;Code.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Share and learn
&lt;/h1&gt;

&lt;p&gt;After I solve a problem I always make sure to check other solutions and explanations, this helps me learn different ways of thinking and problem-solving techniques, also I learned to justify my choices eg: why did I use a LinkedList instead of an Array, etc...&lt;/p&gt;

&lt;p&gt;I didn't mind checking solutions that are written in a different language than the one I use (Kotlin, Java) as that helps me learn more about other languages and different syntax.&lt;/p&gt;

&lt;p&gt;One of the main resources to check other solutions are the Discuss tab on &lt;a href="http://leetcode.com/"&gt;LeetCode&lt;/a&gt;, also &lt;a href="http://geeksforgeeks.org/"&gt;GeeksForGeeks&lt;/a&gt; is a great resource, and sometimes I check some Youtube videos.&lt;/p&gt;

&lt;h1&gt;
  
  
  Documentation
&lt;/h1&gt;

&lt;p&gt;I made sure that I track and document what I learned every day, even if it's simple, this helps me remember and reflect on my past self. and stay motivated to do continue learning and solving problems.&lt;/p&gt;

&lt;h1&gt;
  
  
  Uncomfortable
&lt;/h1&gt;

&lt;p&gt;If you know me you know that I thrive when I see an error or a problem to solve, my friends were always blown away by this. I used to hunt errors and challenges. but after doing Android for a while I got a little comfortable (don't get me wrong there's always something new to learn in Android from Functional programming, Concurrency, new Kotlin features, etc..) But I didn't have the opportunity to challenge myself very ofter. Solving &lt;a href="http://leetcode.com/"&gt;LeetCode&lt;/a&gt; problems brought back that feeling of excitement and eagerness to solve problems, it didn't matter if I fail or if it took me 2 hours to solve I just made sure that I learn something new.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;My goal of this experiment was not to prepare for coding interviews or to apply for FANG companies, My goal was to challenge myself and get out of my comfort zone, and maybe get into Competitive Programming (who knows). I will still do one problem every day and commit to my &lt;a href="https://github.com/anesabml/leetCode"&gt;repository&lt;/a&gt; because it's a habit now and it's been one of the best experiences, as I had the opportunity to fail and thus learn more. I hope this will give you the inspiration to take action and challenge yourself more and just have fun.&lt;/p&gt;

&lt;p&gt;Thank you for reading, I hope you learned something new from this blog post. feel free to reach out on &lt;a href="https://twitter.com/anesabml"&gt;Twitter&lt;/a&gt; if you have any questions. and check out my LeetCode repository &lt;a href="https://github.com/anesabml/leetCode"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
    </item>
    <item>
      <title>Getting started with Zsh</title>
      <dc:creator>Anes Abismail</dc:creator>
      <pubDate>Thu, 02 Jul 2020 12:01:30 +0000</pubDate>
      <link>https://dev.to/anesabml/getting-started-with-zsh-283m</link>
      <guid>https://dev.to/anesabml/getting-started-with-zsh-283m</guid>
      <description>&lt;p&gt;A Shell is a user interface to access the operating system's services, it receives input from the user and executes programs based on that input. To put in simple words a shell is a program that takes input commands from the keyboard and sends them to the operating system to execute.&lt;/p&gt;

&lt;p&gt;On most operating systems a program called &lt;code&gt;Bash&lt;/code&gt; (Bourne Again Shell) acts as the default shell. But there are other shell programs that can be installed in your system that offer more features such as fish and Zsh. In this blog post, I will talk about how to setup Zsh.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is Zsh:
&lt;/h1&gt;

&lt;p&gt;Zsh, also called Z shell, is a Unix shell that can be used as an alternative to Bash and Fish. It's based on &lt;code&gt;Bourne Shell&lt;/code&gt;, allowing it to have the same features as Bash because Bash is also built on top of &lt;code&gt;Bourne Shell&lt;/code&gt;, moreover, it has plenty of features and support for plugins and themes.&lt;/p&gt;

&lt;h1&gt;
  
  
  Zsh features:
&lt;/h1&gt;

&lt;p&gt;Zsh has many features, here is some:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cd (Change directory):&lt;/strong&gt; just type the name of the directory to change your current directory. eg: instead of &lt;code&gt;cd Downloads&lt;/code&gt;, just type &lt;code&gt;Downloads&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spelling correction and completion:&lt;/strong&gt; don't worry if you make a mistake typing the name of a directory, Zsh has spelling correction and completion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plugins and themes:&lt;/strong&gt; Zsh has a rich collection of plugins and themes that you can install to enhance your productivity and make your terminal look cool.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Installing Zsh:
&lt;/h1&gt;

&lt;h2&gt;
  
  
  macOS:
&lt;/h2&gt;

&lt;p&gt;Starting from macOS Catalina, Zsh is installed as the default shell, so you can skip this part. If you are not running macOS Catalina installing Zsh is as simple as running this command Assuming you have &lt;a href="https://brew.sh/"&gt;Homebrew&lt;/a&gt; installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;zsh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To set Zsh as your default shell, execute the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;chsh &lt;span class="nt"&gt;-s&lt;/span&gt; /usr/local/bin/zsh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;for other OS you can check this link &lt;a href="https://github.com/ohmyzsh/ohmyzsh/wiki/Installing-ZSH"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Installing Oh My Zsh:
&lt;/h1&gt;

&lt;p&gt;Zsh alone doesn't do much it, it just like Bash. To start installing plugins and themes you need a tool to manage Zsh configuration and my favourite tool is &lt;a href="https://github.com/ohmyzsh/ohmyzsh"&gt;Oh My Zsh&lt;/a&gt;. Installing Oh My Zsh can be done using either &lt;code&gt;curl&lt;/code&gt; or &lt;code&gt;wget&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using &lt;code&gt;curl&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Using &lt;code&gt;wget&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;wget &lt;span class="nt"&gt;-O-&lt;/span&gt; https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Install plugins and themes:
&lt;/h1&gt;

&lt;p&gt;As stated before Zsh comes with a rich collection of &lt;a href="https://github.com/ohmyzsh/ohmyzsh/wiki/Plugins"&gt;plugins&lt;/a&gt; and &lt;a href="https://github.com/ohmyzsh/ohmyzsh/wiki/Themes"&gt;themes&lt;/a&gt;. let's take a look at how we can enable plugins and themes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plugins:
&lt;/h2&gt;

&lt;p&gt;List of Oh My Zsh plugins can be found &lt;a href="https://github.com/ohmyzsh/ohmyzsh/wiki/Plugins"&gt;here&lt;/a&gt;. If you spot a plugin and you'd like to install, you'll need to enable it in the  &lt;code&gt;~/.zshrc&lt;/code&gt; , this file is the configuration for Zsh, you will find it in your &lt;code&gt;HOME&lt;/code&gt; directory. Open the files with your favourite text editor and enable your favourites plugins by adding a theme to the plugins array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Which plugins would you like to load?&lt;/span&gt;
&lt;span class="c"&gt;# Standard plugins can be found in $ZSH/plugins/&lt;/span&gt;
&lt;span class="c"&gt;# Custom plugins may be added to $ZSH_CUSTOM/plugins/&lt;/span&gt;
&lt;span class="c"&gt;# Example format: plugins=(rails git textmate ruby lighthouse)&lt;/span&gt;
&lt;span class="c"&gt;# Add wisely, as too many plugins slow down shell startup.&lt;/span&gt;
&lt;span class="nv"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;git colored-man-pages colorize brew osx zsh-syntax-highlighting zsh-autosuggestions&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that &lt;a href="https://github.com/zsh-users/zsh-autosuggestions"&gt;zsh-autosuggestions&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/zsh-users/zsh-syntax-highlighting"&gt;zsh-syntax-highlighting&lt;/a&gt; are custom plugins and must be installed manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/zsh-users/zsh-autosuggestions &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ZSH_CUSTOM&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;~/.oh-my-zsh/custom&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/plugins/zsh-autosuggestions
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/zsh-users/zsh-syntax-highlighting.git &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ZSH_CUSTOM&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;~/.oh-my-zsh/custom&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/plugins/zsh-syntax-highlighting
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Themes:
&lt;/h2&gt;

&lt;p&gt;List of Oh My Zsh themes can be found &lt;a href="https://github.com/ohmyzsh/ohmyzsh/wiki/Themes"&gt;here&lt;/a&gt;. In order to enable a theme, you need to set &lt;code&gt;ZSH_THEME&lt;/code&gt; to the name of the theme in your &lt;code&gt;~/.zshrc.&lt;/code&gt; for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set name of the theme to load --- if set to "random", it will&lt;/span&gt;
&lt;span class="c"&gt;# load a random theme each time oh-my-zsh is loaded, in which case,&lt;/span&gt;
&lt;span class="c"&gt;# to know which specific one was loaded, run: echo $RANDOM_THEME&lt;/span&gt;
&lt;span class="nv"&gt;ZSH_THEME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"robbyrussell"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;My favourite theme is &lt;code&gt;powerlevel10k&lt;/code&gt; check it out &lt;a href="https://github.com/romkatv/powerlevel10k"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion:
&lt;/h1&gt;

&lt;p&gt;Zsh is a powerful tool that can boost your productivity and make you life easier, I hope you enjoyed this small tutorial, if you know other plugins and tricks that improved your productivity with using Zsh feel free to reach out on &lt;a href="http://twitter.com/anesabml"&gt;Twitter&lt;/a&gt;, I would like to hear from you.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>terminal</category>
      <category>zsh</category>
      <category>macos</category>
    </item>
    <item>
      <title>Dagger Hilt basics</title>
      <dc:creator>Anes Abismail</dc:creator>
      <pubDate>Tue, 23 Jun 2020 08:05:10 +0000</pubDate>
      <link>https://dev.to/anesabml/dagger-hilt-basics-23g8</link>
      <guid>https://dev.to/anesabml/dagger-hilt-basics-23g8</guid>
      <description>&lt;p&gt;&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@mreichelt?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Marc Reichelt&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/kotlin?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;br&gt;
As you may be aware, Dagger Hilt is the new Dependency Injection library introduced by Google, it's the recommended approach for DI on Android, despite beginning it in Alpha in this small talk I will try to explain some basics about Dagger Hilt.&lt;br&gt;
This article assumes that you are familiar with Architecture components and MVVM.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is Dependency Injection?
&lt;/h2&gt;

&lt;p&gt;Dependency Injection is a technique that allows an object to receive other objects that it depends on, the receiving object is called the client and the provided objects are called service. A Client depends on one Service or more. This helps us follow the SOLID's Single responsibility principle.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Single responsibility principle: means that a class should have only one responsibility and that means it has only one reason to change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ok, you might ask how?&lt;/p&gt;

&lt;p&gt;Let's take an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Repository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;remoteDataSource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RemoteDataSource&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;localDataSource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LocalDataSource&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;localDataSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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="n"&gt;user&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="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;remoteDataSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&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;As you see above the &lt;code&gt;Repository&lt;/code&gt; class has 3 responsibilities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating &lt;code&gt;RemoteDataSource&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;Creating &lt;code&gt;LocalDataSource&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;Retrieving Data from the data sources.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This has many problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;Repository&lt;/code&gt; class can change for a variety of reasons.&lt;/li&gt;
&lt;li&gt; This makes testing the &lt;code&gt;Repository&lt;/code&gt; class much harder, and writing tests is hard enough.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ok, How we are going to fix this? Simply provide the &lt;code&gt;RemoteDataSource&lt;/code&gt; and &lt;code&gt;LocalDataSource&lt;/code&gt; in the constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;remoteDataSource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RemoteDataSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;localDataSource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDataSource&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="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;localDataSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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="n"&gt;user&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="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;remoteDataSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&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;Now as you can see the &lt;code&gt;Repository&lt;/code&gt; class has one responsibility and it's Retrieving Data from the data sources.&lt;/p&gt;

&lt;p&gt;But you will ask how we can create an instance of the &lt;code&gt;Repository&lt;/code&gt; class?&lt;/p&gt;

&lt;p&gt;Let's take a look at our ViewModel&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainViewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;remoteDataSource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RemoteDataSource&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;localDataSource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LocalDataSource&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;remoteDataSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;localDataSource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MutableLiveData&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableLiveData&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;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LiveData&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_user&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And as you might guess the &lt;code&gt;ViewModel&lt;/code&gt; has 4 responsibilities 😲:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating &lt;code&gt;RemoteDataSource&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;Creating &lt;code&gt;LocalDataSource&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;Creating &lt;code&gt;Repository&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;Retrieving Data from the repository.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ok let's fix this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainViewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MutableLiveData&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableLiveData&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;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LiveData&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_user&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Better huh. But wait we don't control the creation of a &lt;code&gt;MainViewModel&lt;/code&gt; instance. &lt;code&gt;ViewModel&lt;/code&gt; is an Android-specific class like Activity, Fragment and Service, so what is the solution?&lt;/p&gt;

&lt;p&gt;Here comes our little friend &lt;code&gt;Hilt&lt;/code&gt; that will solve our problems, but this isn't the only solution before &lt;code&gt;Hilt&lt;/code&gt; developers use &lt;code&gt;ViewModelFactory&lt;/code&gt;, but it introduced a lot of boilerplate code and if you have many ViewModels you will have to do some magic to only use one &lt;code&gt;ViewModelFactory&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;To be able to use Hilt, you need to add some dependencies. Note that at the time of writing this article, the current version is &lt;code&gt;2.28-alpha&lt;/code&gt;. Check out the &lt;a href="https://developer.android.com/training/dependency-injection/hilt-android#setup"&gt;documentation&lt;/a&gt; for the latest version.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build.gradle
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="c1"&gt;// Current version is 2.28-alpha&lt;/span&gt;
        &lt;span class="n"&gt;classpath&lt;/span&gt; &lt;span class="s2"&gt;"com.google.dagger:hilt-android-gradle-plugin:2.28-alpha"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;app/build.gradle:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="nl"&gt;plugin:&lt;/span&gt; &lt;span class="s2"&gt;"kotlin-kapt"&lt;/span&gt;
&lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="nl"&gt;plugin:&lt;/span&gt; &lt;span class="s2"&gt;"dagger.hilt.android.plugin"&lt;/span&gt;

&lt;span class="n"&gt;android&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

        &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"com.google.dagger:hilt-android:2.28-alpha"&lt;/span&gt;
        &lt;span class="n"&gt;kapt&lt;/span&gt; &lt;span class="s2"&gt;"com.google.dagger:hilt-android-compiler:2.28-alpha"&lt;/span&gt;

        &lt;span class="c1"&gt;// For injecting ViewModel&lt;/span&gt;
        &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01"&lt;/span&gt;
        &lt;span class="c1"&gt;// For injecting WorkManager    &lt;/span&gt;
        &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"androidx.hilt:hilt-work:1.0.0-alpha01"&lt;/span&gt;

        &lt;span class="n"&gt;kapt&lt;/span&gt; &lt;span class="s2"&gt;"androidx.hilt:hilt-compiler:1.0.0-alpha01"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  How Hilt works
&lt;/h2&gt;

&lt;p&gt;Hilt works by code generating and it uses different annotations to know how to provide the required dependencies at runtime. let's take a look at some of those annotations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;@Inject: is used to define how the Client would be constructed.&lt;/li&gt;
&lt;li&gt;@ViewModelInject: is used to define how the &lt;code&gt;ViewModel&lt;/code&gt; would be constructed.&lt;/li&gt;
&lt;li&gt;@WorkerInject: is used to define how the &lt;code&gt;Worker&lt;/code&gt; would be constructed.&lt;/li&gt;
&lt;li&gt;@Provides: this is used in a Module class to define how certain dependency is provided (eg: Retrofit, Room database,..)&lt;/li&gt;
&lt;li&gt;@Module: is a class that acts as a bridge between the provided dependency using &lt;code&gt;@Provides&lt;/code&gt; and the consumer class.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hilt example:
&lt;/h2&gt;

&lt;p&gt;Ok let's take an example by and improve our previous solutions, for the sake of simplicity I will remove the data source classes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;    As you can see here I used @Inject annotation.&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;    and as you probably have guessed we need a database instance.&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;*/&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Repository&lt;/span&gt; &lt;span class="nd"&gt;@Inject&lt;/span&gt; &lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MyDatabase&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="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&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;Ok, now how we are going to provide a &lt;code&gt;MyDatabase&lt;/code&gt; instance? since creating a &lt;code&gt;Room&lt;/code&gt; database instance is more than just a constructor we will use the &lt;code&gt;@Provides&lt;/code&gt; annotation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* &lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;    As you can see here I used @Module annotation&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;    Hilt will use this module to call the provideRoomDb function&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;    And get a MyDatabase instance&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;    Nb: we used the object keyword to avoid creating an instance of this module&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;    Will talk about @InstallIn later.&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;*/&lt;/span&gt;
&lt;span class="nd"&gt;@Module&lt;/span&gt;
&lt;span class="nd"&gt;@InstallIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ApplicationComponent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;RoomModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Provides&lt;/span&gt; 
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;provideRoomDb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;MyDatabase&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;MyDatabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;application&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;Now &lt;code&gt;Hilt&lt;/code&gt; nows how to provide &lt;code&gt;MyDatabase&lt;/code&gt; instance and use it to create a &lt;code&gt;Repository&lt;/code&gt; instance. But did you notice something strange? where will Hilt provide an &lt;code&gt;application&lt;/code&gt; instance to create a &lt;code&gt;MyDatabase&lt;/code&gt; instance?&lt;/p&gt;

&lt;p&gt;Hilt comes with a set of default bindings that can be injected as dependencies, and &lt;code&gt;Application&lt;/code&gt; is one of those bindings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Components:
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;RoomModule&lt;/code&gt; above I used &lt;code&gt;@InstallIn(ApplicationComponent::class)&lt;/code&gt;. what the hell is it?&lt;/p&gt;

&lt;p&gt;It tells Hilt where to install the Module meaning where it should be available as a dependency, in this example, we want to be able to use &lt;code&gt;MyDatabase&lt;/code&gt; across the Application so we use the &lt;code&gt;ApplicationComponent&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;There are multiple components and using them depends on how you want to scope the provided dependency:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ApplicationComponent.&lt;/li&gt;
&lt;li&gt;ServiceComponent.&lt;/li&gt;
&lt;li&gt;ActivityRetainedComponent.&lt;/li&gt;
&lt;li&gt;ActivityComponent.&lt;/li&gt;
&lt;li&gt;FragmentComponent.&lt;/li&gt;
&lt;li&gt;ViewComponent.&lt;/li&gt;
&lt;li&gt;ViewWithFragmentComponent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kZwAOmK_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dagger.dev/hilt/component-hierarchy.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kZwAOmK_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dagger.dev/hilt/component-hierarchy.svg" alt="Dagger Hilt components"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can take a look at the &lt;a href="https://dagger.dev/hilt/components"&gt;documentation&lt;/a&gt; and read more about each component.&lt;/p&gt;

&lt;h2&gt;
  
  
  ViewModel:
&lt;/h2&gt;

&lt;p&gt;Now let's learn something new with &lt;code&gt;MainViewModel&lt;/code&gt;. As you can see here I used &lt;code&gt;@ViewModelInject&lt;/code&gt; annotation, this will tell Hilt that is a ViewModel and since ViewModels survive configuration changes, Hilt will make sure to return the same instance after configuration changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* &lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;    As you can see here I used @ViewModelInject annotation&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;    it will tell Hilt that is a ViewModel class and since &lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;    ViewModels survive configuration changes, Hilt will make sure to return&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;    the same instance after configuration changes.&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;*/&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainViewModel&lt;/span&gt; &lt;span class="nd"&gt;@ViewModelInject&lt;/span&gt; &lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Repository&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;remoteDataSource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RemoteDataSource&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;localDataSource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LocalDataSource&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;remoteDataSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;localDataSource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MutableLiveData&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableLiveData&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;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LiveData&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_user&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and Now let's see some magic. In order to connect the dots we need to create an Application class and annotate it with &lt;code&gt;@HiltAndroidApp&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@HiltAndroidApp&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApplication&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="c1"&gt;// Nb: Hilt injects the dependencies in super.onCreate() &lt;/span&gt;
     &lt;span class="c1"&gt;// if you override onCreate make sure you call super.onCreate()&lt;/span&gt;
     &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCreate&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;h2&gt;
  
  
  Entry Points:
&lt;/h2&gt;

&lt;p&gt;Now after we told &lt;code&gt;Hilt&lt;/code&gt; how to provide some dependencies, it's time to use those dependencies, for this example I will need &lt;code&gt;Hilt&lt;/code&gt; to provide me with the &lt;code&gt;MainViewModel&lt;/code&gt; instance, &lt;code&gt;@AndroidEntryPoint&lt;/code&gt; will take care of that, which will take care of injecting the required dependencies for our activity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@AndroidEntryPoint&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainActivity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AppCompatActivity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Inject&lt;/span&gt; &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MainViewModel&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;viewModels&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Nb: Hilt injects the dependencies in super.onCreate() &lt;/span&gt;
       &lt;span class="c1"&gt;// if you override onCreate make sure you call super.onCreate()&lt;/span&gt;

        &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// will throw a runtime exeption&lt;/span&gt;

        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;setContentView&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;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;activity_main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomething&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;And we are done. &lt;code&gt;Hilt&lt;/code&gt; is much bigger than just what I coved in this article, but this is the should get you started.&lt;/p&gt;

&lt;p&gt;Dependency Injection libraries can be hard to understand, so if you have any question feel free to reach me out on Twitter &lt;a href="http://twitter.com/anesabml"&gt;@anesabml&lt;/a&gt; I will happily answer your questions.&lt;br&gt;
Lastly, if this article has helped you learn something new, feel free to share it with others and help them learn as well.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dagger.dev/hilt/"&gt;Dagger Hilt documentation.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>dagger</category>
      <category>di</category>
    </item>
  </channel>
</rss>
