<?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>Forem: A.J. Kueterman</title>
    <description>The latest articles on Forem by A.J. Kueterman (@robotsquidward).</description>
    <link>https://forem.com/robotsquidward</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%2F132562%2F16007888-3337-47da-9ddc-3b632dc41c1b.jpeg</url>
      <title>Forem: A.J. Kueterman</title>
      <link>https://forem.com/robotsquidward</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/robotsquidward"/>
    <language>en</language>
    <item>
      <title>Create Your Own LOGAF Scale for Pull Request Reviews</title>
      <dc:creator>A.J. Kueterman</dc:creator>
      <pubDate>Fri, 03 Nov 2023 21:37:00 +0000</pubDate>
      <link>https://forem.com/robotsquidward/create-your-own-logaf-scale-for-pull-request-reviews-504o</link>
      <guid>https://forem.com/robotsquidward/create-your-own-logaf-scale-for-pull-request-reviews-504o</guid>
      <description>&lt;p&gt;I like the idea of the &lt;a href="https://blog.danlew.net/2020/04/15/the-logaf-scale/" rel="noopener noreferrer"&gt;LOGAF scale&lt;/a&gt;, where we can easily indicate if suggestions to PR review or other feedback is given in '&lt;em&gt;levels of give-a-f*ck&lt;/em&gt;'. A scale like this helps collaborators prioritize and contextualize your feedback quickly in a way that's pretty straightforward.&lt;/p&gt;

&lt;p&gt;While I love the raw LOGAF model, I do sometimes hesitate to share this philosophy with everyone at my company based on varying levels of comfort with profanity. Instead, I took the time just to indicate my own low/medium/high 'level of caring' that I can share on PR reviews. I also took the time to briefly spell out what I expect from each level. Now in PRs I can include a 'LOGAF level' link and move on.&lt;/p&gt;

&lt;p&gt;Below is my basic scale of 3 levels, the description of each, and the link I'd include in each comment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Low
&lt;/h2&gt;

&lt;p&gt;A this level I usually just noticed something that had a slight code smell. I may spot things that are tangential to your change or just things I 'noticed' when reviewing. &lt;/p&gt;

&lt;p&gt;You can self-resolve these comments in GitHub without a comment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[🟢 low](https://dev.to/robotsquidward/create-your-own-logaf-scale-for-pull-request-reviews-504o/#low)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Medium
&lt;/h2&gt;

&lt;p&gt;At this level I feel changes should be made but can be convinced with a good reason, a follow up story/documentation/etc. that captures the changes in future work or clarification. &lt;/p&gt;

&lt;p&gt;You can self-resolve these comments in GitHub with a comment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[🟡 medium](https://dev.to/robotsquidward/create-your-own-logaf-scale-for-pull-request-reviews-504o/#medium)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  High
&lt;/h2&gt;

&lt;p&gt;At this level I feel like changes need to be made before this PR is merged. In exceptional cases, follow up stories &amp;amp; documentation may resolve these concerns, but they would usually require a conversation with me or some other SME.&lt;/p&gt;

&lt;p&gt;Do not self-resolve these comments in GitHub. Make changes / leave comments and allow me to resolve them before merging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[🔴 high](https://dev.to/robotsquidward/create-your-own-logaf-scale-for-pull-request-reviews-504o/#high)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If you decide to implement your own LOGAF scale for your PR reviews let me know on Mastodon &lt;a href="https://androiddev.social/@aj" rel="noopener noreferrer"&gt;@aj on androiddev.social&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other PR Review documentation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://deepsource.com/blog/code-review-best-practices" rel="noopener noreferrer"&gt;Code review best practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>logaf</category>
      <category>github</category>
      <category>engineering</category>
      <category>pr</category>
    </item>
    <item>
      <title>How to Use TalkBack on an Android Emulator</title>
      <dc:creator>A.J. Kueterman</dc:creator>
      <pubDate>Fri, 28 Apr 2023 17:48:00 +0000</pubDate>
      <link>https://forem.com/robotsquidward/how-to-use-talkback-on-an-android-emulator-1f33</link>
      <guid>https://forem.com/robotsquidward/how-to-use-talkback-on-an-android-emulator-1f33</guid>
      <description>&lt;p&gt;A huge part of making our apps accessible is enabling access for those who have limited vision. This can range from allowing users to adjust the size of the UI or the text in our apps - to making sure that accessibility tools like screen readers can parse the UI of our apps.&lt;/p&gt;

&lt;p&gt;The screen reading tool provided by Google on Android devices is called TalkBack. This app is usually installed by default on Android devices, letting users operate their phones without needing to see the screen. For developers, we can use TalkBack to &lt;a href="https://developer.android.com/guide/topics/ui/accessibility" rel="noopener noreferrer"&gt;ensure our app is accessible&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, you may have noticed that this behavior isn't available by default when you create a new Android Emulator in Android Studio - a problem for developers &amp;amp; testers that don't have access to physical devices.&lt;/p&gt;

&lt;p&gt;Let's walk through the process of getting TalkBack set up on your Android Emulators.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create A New Emulator
&lt;/h2&gt;

&lt;p&gt;In Android Studio (I'm using Android Studio Flamingo 2022.2.1), open the Device Manager and choose Create Device.&lt;/p&gt;

&lt;h3&gt;
  
  
  Select Hardware
&lt;/h3&gt;

&lt;p&gt;Select a device you'd like to create an Emulator of, &lt;strong&gt;making sure to select one that has Play Store access&lt;/strong&gt;. You can tell by the Play Store icon visible in the 'Play Store' column of the selection list.&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%2Fzwqz4hro4bsenqy759qa.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%2Fzwqz4hro4bsenqy759qa.png" alt="selecting play store enabled device screen shot" width="800" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After selecting the appropriate hardware, click Next.&lt;/p&gt;

&lt;h3&gt;
  
  
  System Image
&lt;/h3&gt;

&lt;p&gt;Select or download an Android OS image. I chose to download &amp;amp; select Android Tiramisu (API 33), though any modern OS will do.&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%2Fw2xczvvwirw6im8y6e96.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%2Fw2xczvvwirw6im8y6e96.png" alt="selecting system image screen shot" width="800" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once that is downloaded &amp;amp; selected, click Next.&lt;/p&gt;

&lt;h3&gt;
  
  
  AVD Setup
&lt;/h3&gt;

&lt;p&gt;We've done all the hard work, no changes needed here unless you need to make specific adjustments to your Emulator setup. Click Finish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Download the Android Accessibility Suite
&lt;/h2&gt;

&lt;p&gt;Back in Device Manager, click the 'play' button on your newly added device to run it.&lt;/p&gt;

&lt;p&gt;If, at this point, we decided to go into Settings and look for TalkBack, we won't get any results. This is the default behavior for all new emulators you create. Let's fix that by downloading the &lt;a href="https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback&amp;amp;hl=en_US&amp;amp;gl=US" rel="noopener noreferrer"&gt;Android Accessibility Suite&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sign In &amp;amp; Search
&lt;/h3&gt;

&lt;p&gt;Your device should have the Play Store app already installed. Open it, and sign in with your Google account. You may have to go through a few setup steps when logging in to the Play Store, as this emulator is treated as a 'new' device.&lt;/p&gt;

&lt;p&gt;In the Play Store search for 'android accessibility suite' and download it.&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%2F4kkhzk9lynkijatlbhtr.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%2F4kkhzk9lynkijatlbhtr.png" alt="searching for android accessibility suite" width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using TalkBack
&lt;/h3&gt;

&lt;p&gt;After it downloads and installs, close the Play Store and look at your list of installed apps. You may notice that the Accessibility Suite doesn't show up here. That's expected for this very specific app.&lt;/p&gt;

&lt;p&gt;Instead, open Settings, and search for TalkBack. Here you'll see the TalkBack option, which brings you to the Screen Reader section of the device's Accessibility settings.&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%2Fbuqlwieudksic5t6t6z1.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%2Fbuqlwieudksic5t6t6z1.png" alt="accessibility settings on device with TalkBack" width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ta-da! 🥳 Now we can use TalkBack just like we would on a normal Android device.&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%2Fz1zsc28zl6dnotesc97x.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%2Fz1zsc28zl6dnotesc97x.png" alt="talkback landing page" width="800" height="798"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  TalkBack Resources
&lt;/h3&gt;

&lt;p&gt;For more on testing your apps for accessibility with TalkBack, check out these great resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=_1yRVwhEv5I" rel="noopener noreferrer"&gt;TalkBack - Accessibility on Android (video)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://accessibility.huit.harvard.edu/test-android-talkback" rel="noopener noreferrer"&gt;Testing with TalkBack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/guide/topics/ui/accessibility/testing" rel="noopener noreferrer"&gt;Google's Guide to Testing Accessibility&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Go forth and build accessible apps!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This post was originally shared at &lt;a href="https://ajkueterman.dev/posts/how-to-use-talkback-on-an-android-emulator/" rel="noopener noreferrer"&gt;ajkueterman.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>android</category>
      <category>emulator</category>
      <category>talkback</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Android ViewModel: Manual Dependency Injection Made Easy</title>
      <dc:creator>A.J. Kueterman</dc:creator>
      <pubDate>Mon, 28 Sep 2020 17:29:12 +0000</pubDate>
      <link>https://forem.com/robotsquidward/android-viewmodel-manual-dependency-injection-made-easy-3cnk</link>
      <guid>https://forem.com/robotsquidward/android-viewmodel-manual-dependency-injection-made-easy-3cnk</guid>
      <description>&lt;p&gt;If you want to go right to a library that enables easy View Model dependency injection, check out &lt;a href="https://github.com/robotsquidward/lazyviewmodels" rel="noopener noreferrer"&gt;Lazy ViewModels&lt;/a&gt; on &lt;a href="https://github.com/robotsquidward" rel="noopener noreferrer"&gt;my GitHub&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;The Android team has been increasingly vocal about their support for &lt;a href="https://en.wikipedia.org/wiki/Dependency_injection" rel="noopener noreferrer"&gt;Dependency Injection&lt;/a&gt; frameworks like &lt;a href="https://dagger.dev/" rel="noopener noreferrer"&gt;Dagger&lt;/a&gt;, going so far as to develop and recommend &lt;a href="https://developer.android.com/training/dependency-injection/hilt-android" rel="noopener noreferrer"&gt;Hilt&lt;/a&gt; - their Android DI framework built on top of Dagger - for modern Android development.&lt;/p&gt;

&lt;p&gt;In their guide to &lt;a href="https://developer.android.com/training/dependency-injection/manual" rel="noopener noreferrer"&gt;manual dependency injection&lt;/a&gt; the Android team lays out approaches to manual DI for View Models. They offer both the basic approach to manual DI - just instantiating everything you need in &lt;code&gt;onCreate&lt;/code&gt; and using &lt;code&gt;lateinit var&lt;/code&gt; View Models - and the container approach using a custom &lt;code&gt;AppContainer&lt;/code&gt; to handle dependencies across all your Activities.&lt;/p&gt;

&lt;p&gt;The alternative they give to this boiler-plate-heavy approach is to recommend Dagger or Hilt to handle this process for you. However, in many apps, pulling in a DI framework is overhead you really don't need. Instead, what if there was a way to manually inject dependencies into your Android Activity &amp;amp; Fragment View Models without all the boiler plate?&lt;/p&gt;

&lt;h2&gt;
  
  
  Lazy DI Using Android Lifecycle ViewModel Extensions
&lt;/h2&gt;

&lt;p&gt;One of the ways  the Android Fragment &amp;amp; Lifecycle teams have tried to make the View Model easier to use in Activities and Fragments is providing the &lt;a href="https://mvnrepository.com/artifact/androidx.lifecycle/lifecycle-viewmodel-ktx" rel="noopener noreferrer"&gt;Android Lifecycle ViewModel Kotlin Extensions&lt;/a&gt; library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This library lets you instantiate a View Model in a Fragment or Activity with a delegate method - making it easy to create a properly scoped View Model.&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyActivity&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="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MyViewModel&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="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUsers&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Observer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&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;&amp;gt;{&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="c1"&gt;// update UI&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;This is beautiful! However, what if our View Model needs to use a &lt;a href="https://developer.android.com/jetpack/guide#overview" rel="noopener noreferrer"&gt;repository&lt;/a&gt; to make a call to &lt;a href="https://square.github.io/retrofit/" rel="noopener noreferrer"&gt;Retrofit&lt;/a&gt;? We would like to inject that repository into our View Model when we construct it.&lt;/p&gt;

&lt;h2&gt;
  
  
  View Model Provider Factory
&lt;/h2&gt;

&lt;p&gt;One way to enable this behavior is to use &lt;a href="https://developer.android.com/reference/androidx/lifecycle/ViewModelProvider.Factory" rel="noopener noreferrer"&gt;&lt;code&gt;ViewModelProvider.Factory&lt;/code&gt;&lt;/a&gt;, with which you can instantiate a View Model with it's needed dependencies. To do so, you need to create your own Factory that extends the &lt;code&gt;ViewModelProvider.Factory&lt;/code&gt; interface, or create a function that can return an &lt;code&gt;object&lt;/code&gt; that overrides the &lt;code&gt;Factory.create&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;If we wanted to stop here, we could create a Kotlin function to do this for us:&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;createWithFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;ViewModelProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Factory&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="err"&gt;: &lt;/span&gt;&lt;span class="nc"&gt;ViewModelProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Factory&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&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;?&amp;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;modelClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;):&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;@Suppress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UNCHECKED_CAST"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="c1"&gt;// Casting T as ViewModel&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;T&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 use it in our Activity or Fragment like this:&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;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MyViewModel&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;lazy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;ViewModelProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nf"&gt;createWithFactory&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;MyViewModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MyRepository&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="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MyViewModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now create a View Model with the necessary dependencies! However, even with our Factory abstracted into a function, we still need to manage the boiler plate of  &lt;code&gt;ViewModelProvider&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  View Model Provider &amp;amp; Kotlin Extension Mashup
&lt;/h2&gt;

&lt;p&gt;Remember, we could use &lt;code&gt;by viewModels&lt;/code&gt; or &lt;code&gt;by activityViewModels&lt;/code&gt; to delegate the creation of our View Models, but we weren't able to inject our required dependencies without a Factory.&lt;/p&gt;

&lt;p&gt;Now that we have a handy way to instantiate a View Model with a Factory, we can let these Kotlin Extensions for View Model to hide this &lt;code&gt;ViewModelProvider&lt;/code&gt; boiler plate. Here's how it would look in a Fragment.&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;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MyViewModel&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;activityViewModels&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;createWithFactory&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;MyViewModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MyRepository&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;Almost perfect. All we have to do is provide a Factory and a lambda that returns our View Model. But, with a little help from the &lt;code&gt;lifecycle-viewmodel-ktx&lt;/code&gt; library and Kotlin extension functions, we can take it one step further.&lt;/p&gt;

&lt;h2&gt;
  
  
  Endgame: Easy View Model DI
&lt;/h2&gt;

&lt;p&gt;Let's use the power of extension functions, our Factory knowledge, and the &lt;code&gt;ViewModelLazy&lt;/code&gt; class to delegate the creation of our View Model to functions on Activity and Fragment.&lt;/p&gt;

&lt;p&gt;We provide the function to create our View Model as a lambda that returns type &lt;code&gt;&amp;lt;VM : ViewModel&amp;gt;&lt;/code&gt;, and the &lt;code&gt;viewModelBuilder&lt;/code&gt; and &lt;code&gt;activityViewModelBuilder&lt;/code&gt; extensions do the rest.&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="cm"&gt;/**
 * Get a [ViewModel] in an [ComponentActivity].
 */&lt;/span&gt;
&lt;span class="nd"&gt;@MainThread&lt;/span&gt;
&lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;reified&lt;/span&gt; &lt;span class="nc"&gt;VM&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;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;ComponentActivity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;viewModelBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;noinline&lt;/span&gt; &lt;span class="n"&gt;viewModelInitializer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;VM&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;VM&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ViewModelLazy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;viewModelClass&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VM&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;storeProducer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;viewModelStore&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;factoryProducer&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="nd"&gt;@ViewModelLazy&lt;/span&gt; &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="err"&gt;: &lt;/span&gt;&lt;span class="nc"&gt;ViewModelProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Factory&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&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;?&amp;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;modelClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;):&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;@Suppress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UNCHECKED_CAST"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="c1"&gt;// Casting T as ViewModel&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;viewModelInitializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;T&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;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Get a [ViewModel] in a [Fragment].
 */&lt;/span&gt;
&lt;span class="nd"&gt;@MainThread&lt;/span&gt;
&lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;reified&lt;/span&gt; &lt;span class="nc"&gt;VM&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;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;activityViewModelBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;noinline&lt;/span&gt; &lt;span class="n"&gt;viewModelInitializer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;VM&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;VM&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ViewModelLazy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;viewModelClass&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VM&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;storeProducer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;requireActivity&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;viewModelStore&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;factoryProducer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="err"&gt;: &lt;/span&gt;&lt;span class="nc"&gt;ViewModelProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Factory&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&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;?&amp;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;modelClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;):&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;@Suppress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UNCHECKED_CAST"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="c1"&gt;// Casting T as ViewModel&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;viewModelInitializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;T&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in our Activity:&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;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MyViewModel&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;viewModelBuilder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;MyViewModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MyRepository&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;Or Fragment:&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;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MyViewModel&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;activityViewModelBuilder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;MyViewModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MyRepository&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 can easily build a ViewModel, along with all it's necessary dependencies, all without the annoying boilerplate!&lt;/p&gt;

&lt;p&gt;If you're in a situation where you need complex Android View Models, want to follow SOLID principles of dependency injection, and you're not quite ready to adopt the complexity of a full Dependency Injection Framework - hopefully this helps present some other options that can make the task easier to manage.&lt;/p&gt;

&lt;p&gt;If you have thoughts on the Android View Model or want to share your approaches to manual DI, don't hesitate to reach out &lt;a href="https://twitter.com/ajkueterman" rel="noopener noreferrer"&gt;@ajkueterman&lt;/a&gt; on Twitter, or follow me on &lt;a href="https://dev.to/robotsquidward"&gt;DEV.to&lt;/a&gt; and leave a comment on this story.&lt;/p&gt;




&lt;p&gt;This story was originally published on my blog, at &lt;a href="https://ajkueterman.dev/posts/android-viewmodel-manual-dependency-injection-made-easy/" rel="noopener noreferrer"&gt;ajkueterman.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Hacker News Compose</title>
      <dc:creator>A.J. Kueterman</dc:creator>
      <pubDate>Sun, 12 Jul 2020 19:41:14 +0000</pubDate>
      <link>https://forem.com/robotsquidward/hacker-news-compose-16p5</link>
      <guid>https://forem.com/robotsquidward/hacker-news-compose-16p5</guid>
      <description>&lt;p&gt;I recently gave a talk and live code-along session that presented an introduction to &lt;a href="https://developer.android.com/jetpack/compose" rel="noopener noreferrer"&gt;Jetpack Compose&lt;/a&gt;. The talk was aimed at introducing developers of all levels to Jetpack Compose, so that we could learn how to build a starter Android app using this new UI technology. We started with an empty Android app, and ended with a fully-functioning &lt;a href="https://news.ycombinator.com" rel="noopener noreferrer"&gt;Hacker News&lt;/a&gt; client that fetched top stories and let users save their favorite articles.&lt;/p&gt;

&lt;p&gt;If you're interested in Jetpack Compose, and want to get started with a high-level intro and a straightforward example app, then check out my repo on GitHub - &lt;a href="https://github.com/robotsquidward/hn-compose" rel="noopener noreferrer"&gt;&lt;code&gt;hn-compose&lt;/code&gt;&lt;/a&gt;. It includes the slides from my talk, as well as a multi-part code example that walks through the process of building out our Hacker News client.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/robotsquidward/hn-compose" rel="noopener noreferrer"&gt;&lt;img alt="Screen Shot 2020-07-12 at 3 28 34 PM" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F10855762%2F87254925-67715900-c454-11ea-8378-77ec2e5eedb8.png" width="800" height="747"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check it out, and feel free to reach out to me on Twitter &lt;a href="https://twitter.com/ajkueterman" rel="noopener noreferrer"&gt;@ajkueterman&lt;/a&gt; or leave a comment on my post in &lt;a href="https://news.ycombinator.com/item?id=23814095" rel="noopener noreferrer"&gt;Hacker News&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://ajkueterman.dev/posts/hacker-news-jetpack-compose/" rel="noopener noreferrer"&gt;AJ.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>compose</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Safely Launch Exception-Ready Coroutines</title>
      <dc:creator>A.J. Kueterman</dc:creator>
      <pubDate>Wed, 11 Mar 2020 20:52:36 +0000</pubDate>
      <link>https://forem.com/robotsquidward/safely-launch-exception-ready-coroutines-2j9b</link>
      <guid>https://forem.com/robotsquidward/safely-launch-exception-ready-coroutines-2j9b</guid>
      <description>&lt;p&gt;Launching &lt;code&gt;suspend&lt;/code&gt; functions in Kotlin can be a complicated affair. Managing your &lt;code&gt;CoroutineScope&lt;/code&gt; and making sure exceptions are handled properly can be confusing and easy to forget.&lt;/p&gt;

&lt;p&gt;In Android, when using the &lt;code&gt;ViewModel&lt;/code&gt; or &lt;code&gt;Lifecycle&lt;/code&gt; specific scopes this gets much easier. We let the Android system provide a &lt;code&gt;CoroutineScope&lt;/code&gt; and manage killing our coroutines when the lifecycle of those things end.&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;getObjectFromNetwork&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;networkRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getObject&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;However, there are cases when exceptions can be thrown from the &lt;code&gt;CoroutineScope&lt;/code&gt;. A real life example I experienced recently was a &lt;code&gt;SocketTimeoutException&lt;/code&gt; that was thrown from a Retrofit call I was making using a &lt;code&gt;suspend&lt;/code&gt; function. The result is &lt;a href="https://github.com/Kotlin/kotlinx.coroutines/issues/753" rel="noopener noreferrer"&gt;an Android app crash&lt;/a&gt;, which is definitely not desired when network calls can result in many different thrown exceptions.&lt;/p&gt;

&lt;p&gt;The Kotlin &lt;a href="https://kotlinlang.org/docs/reference/coroutines/exception-handling.html#coroutineexceptionhandler" rel="noopener noreferrer"&gt;&lt;code&gt;CoroutineExceptionHandler&lt;/code&gt;&lt;/a&gt; can help us more easily handle exceptions thrown from our Coroutine scope, but it requires us to register the exception handler when we &lt;code&gt;launch&lt;/code&gt; a new coroutine so we &lt;a href="https://proandroiddev.com/managing-exceptions-in-nested-coroutine-scopes-9f23fd85e61" rel="noopener noreferrer"&gt;properly handle nested exceptions&lt;/a&gt;.&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;coroutineExceptionHandler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoroutineExceptionHandler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;coroutineContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// handle thrown exceptions from coroutine scope&lt;/span&gt;
  &lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;printStackTrace&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;getObjectFromNetwork&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;coroutineExceptionHandler&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;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;networkRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getObject&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;Now, while the &lt;code&gt;ViewModel&lt;/code&gt; is probably a logical place for handling exceptions in network calls, there are a lot of exceptions that are thrown irregularly from your app that aren't part of the logical flow of a network call. Using Retrofit as an example, most network calls should return a &lt;a href="https://square.github.io/retrofit/2.x/retrofit/retrofit2/Response.html" rel="noopener noreferrer"&gt;&lt;code&gt;Response&lt;/code&gt;&lt;/a&gt; with a &lt;code&gt;body()&lt;/code&gt; or &lt;code&gt;errorBody()&lt;/code&gt; to handle instead of edge-cases where actual exceptions, like a &lt;code&gt;SocketTimeoutException&lt;/code&gt; is thrown. It might be worth it to you to abstract this error handling away from your &lt;code&gt;ViewModel&lt;/code&gt; to help minimize boiler plate.&lt;/p&gt;

&lt;p&gt;To try this out, let's leverage the power of Kotlin extensions to create a &lt;code&gt;safeLaunch&lt;/code&gt; method on &lt;code&gt;CoroutineScope&lt;/code&gt; that can apply a default &lt;code&gt;CoroutineExceptionHandler&lt;/code&gt;.&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="nc"&gt;CoroutineScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeLaunch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;launchBody&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Job&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;coroutineExceptionHandler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoroutineExceptionHandler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="n"&gt;coroutineContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// handle thrown exceptions from coroutine scope&lt;/span&gt;
    &lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;printStackTrace&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="k"&gt;this&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;coroutineExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="n"&gt;launchBody&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&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;Now, we can call &lt;code&gt;safeLaunch&lt;/code&gt; on any &lt;code&gt;CoroutineScope&lt;/code&gt;, like our &lt;code&gt;viewModelScope&lt;/code&gt;, to launch a coroutine with this default error handling behavior.&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;getObjectFromNetwork&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;safeLaunch&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;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;networkRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getObject&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;There you have it! A nicely-encapsulated method to launch &lt;code&gt;suspend&lt;/code&gt; functions knowing that we won't see random app crashes because of an unhandled &lt;code&gt;Throwable&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;If we wanted &lt;code&gt;safeLaunch&lt;/code&gt; to be the core &lt;code&gt;CoroutineScope&lt;/code&gt; launch method in our app, we can even improve the extension a bit to allow users the flexibility of passing their own &lt;code&gt;CoroutineExceptionHandler&lt;/code&gt; instead of using the default.&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;coroutineExceptionHandler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoroutineExceptionHandler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;coroutineContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;   
  &lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;printStackTrace&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;CoroutineScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeLaunch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;exceptionHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CoroutineExceptionHandler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coroutineExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;launchBody&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Job&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&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;exceptionHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="n"&gt;launchBody&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&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;The same day I wrote this article, &lt;a href="https://medium.com/@manuelvicnt" rel="noopener noreferrer"&gt;Manuel Vivo&lt;/a&gt; and &lt;a href="https://medium.com/@florina.muntenescu" rel="noopener noreferrer"&gt;Florina Muntenescu&lt;/a&gt; from the Android developer relations team released a really good series on coroutines, including the subject of coroutine exceptions. Check it out if you want to learn more about coroutines and how to manage them.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://medium.com/androiddevelopers/coroutines-first-things-first-e6187bf3bb21" rel="noopener noreferrer"&gt;Intro to Coroutines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/androiddevelopers/cancellation-in-coroutines-aa6b90163629" rel="noopener noreferrer"&gt;Cancelling Coroutines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/androiddevelopers/exceptions-in-coroutines-ce8da1ec060c" rel="noopener noreferrer"&gt;Exceptions in Coroutines&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://ajkueterman.dev/posts/safely-launch-exception-ready-coroutines/" rel="noopener noreferrer"&gt;X-post from ajkueterman.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>coroutines</category>
    </item>
    <item>
      <title>Dealing with Deprecation &amp; iOS Headers</title>
      <dc:creator>A.J. Kueterman</dc:creator>
      <pubDate>Wed, 05 Feb 2020 02:38:21 +0000</pubDate>
      <link>https://forem.com/robotsquidward/dealing-with-deprecation-ios-headers-282</link>
      <guid>https://forem.com/robotsquidward/dealing-with-deprecation-ios-headers-282</guid>
      <description>&lt;p&gt;Recently, I started getting emails from GitHub letting me know that my GitHub OAuth app was accessing GitHub API's using a deprecated authentication method.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1224527769046077440-650" src="https://platform.twitter.com/embed/Tweet.html?id=1224527769046077440"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1224527769046077440-650');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1224527769046077440&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;My app, &lt;a href="https://apps.apple.com/us/app/octonote/id1433164731" rel="noopener noreferrer"&gt;OctoNote&lt;/a&gt;, uses the GitHub gists API to store and manage notes as GitHub gists. I had originally &lt;a href="https://ajkueterman.dev/posts/ios13-authentication-services-changes/" rel="noopener noreferrer"&gt;set up OAuth&lt;/a&gt; for OctoNote using an &lt;code&gt;access_token&lt;/code&gt; as a URL query parameter, which is &lt;a href="https://developer.github.com/changes/2019-11-05-deprecated-passwords-and-authorizations-api/#authenticating-using-query-parameters" rel="noopener noreferrer"&gt;no longer supported&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Instead, we need to add the token as an &lt;a href="https://developer.github.com/changes/2019-11-05-deprecated-passwords-and-authorizations-api/#authenticating-using-query-parameters" rel="noopener noreferrer"&gt;&lt;code&gt;Authorization&lt;/code&gt; header&lt;/a&gt; on the request. Doing so in iOS is easy, just specify the value of the token and the name of the Header field (in this case &lt;code&gt;Authorization&lt;/code&gt;) on your API call requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"token &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;token_val&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;forHTTPHeaderField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After doing this, your regular calls to the GitHub API's should still work as expected, but you should no longer get deprecation notices from GitHub! 🎉&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swift</category>
      <category>github</category>
      <category>oauth</category>
    </item>
    <item>
      <title>Android Library Lessons Learned</title>
      <dc:creator>A.J. Kueterman</dc:creator>
      <pubDate>Fri, 13 Dec 2019 16:00:04 +0000</pubDate>
      <link>https://forem.com/robotsquidward/android-library-lessons-learned-3pm6</link>
      <guid>https://forem.com/robotsquidward/android-library-lessons-learned-3pm6</guid>
      <description>&lt;p&gt;Recently, I worked on an Android project where we wished to abstract away some Android resource files from the root application. Instead, we wanted our app - and any subsequent app - to consume Android resources (colors, strings, dimensions) from a library. In addition, because we wanted to define these resources independently of the apps consuming them, we needed to build our library separate from our main application.&lt;/p&gt;

&lt;p&gt;After some effort, and some trials working with Gradle and Android dependencies, we were able to build our Android library for distribution to consuming apps. Let's take a minute and talk about what was required to accomplish this, and point out some pitfalls to help you build your first external Android library.&lt;/p&gt;

&lt;h2&gt;
  
  
  JAR/AAR
&lt;/h2&gt;

&lt;p&gt;A bundle of Java/Kotlin source code without any Android dependencies can be packaged and consumed as a &lt;code&gt;.jar&lt;/code&gt; file. This makes things simple and enables cross platform code that isn't dependent on Android.&lt;/p&gt;

&lt;p&gt;However, we immediately realized we'd need access to Android stuff in our project. We need access to Android resources, which can only be accessed in an &lt;a href="https://developer.android.com/studio/projects/android-library#CreateLibrary" rel="noopener noreferrer"&gt;Android Library&lt;/a&gt;. Because of this, we know that we'll need to build an Android Archive (&lt;code&gt;.aar&lt;/code&gt;) file.&lt;/p&gt;

&lt;p&gt;An AAR file can't be &lt;code&gt;tar&lt;/code&gt; or &lt;code&gt;jar&lt;/code&gt;'d up easily from the command line. Because it's an Android Library (&lt;code&gt;'com.android.library'&lt;/code&gt;) it needs to be built using Gradle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gradle Dependencies
&lt;/h2&gt;

&lt;p&gt;In our case, we were using a Node.js project to output our Android Library resources. This meant that we had a separate, non-Android app, building our eventual AAR output.&lt;/p&gt;

&lt;p&gt;The first attempt at this used a packaged version of the Gradle wrapper from a dependency file on the machine, and attempted to package up a library from it's source code defined in the Node app. We quickly ran into issues here, because defining a Library build.gradle file without a parent build.gradle is something of an anti-pattern for an Android project structure.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Normally you have a 'project' build.gradle file at the root, then an application (usually 'app') module with it's own build.gradle file. Then, each module defined in the project would have it's own module build.gradle file. Trying to build a module without defining a parent 'project' build.gradle file isn't the norm - and was causing problems in our implementation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, what we ended up needing to do was build a shell Android app project with an included sub-module. The shell project defined the Android project structure at a high level, including our library sub-module structure, and was zipped up and packaged with our Node app. Then, as part of our Node app's build process, we would un-zip the Android project and update the sub-module directories to prepare our library. Finally, we would use the included Gradle wrapper in our Library container project to build our submodule.&lt;/p&gt;

&lt;p&gt;Besides the clunky-ness of creating an entire Android application project just to build a module - this worked like a charm!&lt;/p&gt;

&lt;h2&gt;
  
  
  Android Dependencies
&lt;/h2&gt;

&lt;p&gt;After fixing our project structure and defining the proper Gradle files, our app successfully built and packaged an AAR file. However, importing this file into an Android project didn't correctly link Android Resources.&lt;/p&gt;

&lt;p&gt;What we soon learned was that including Android Resources is part of the Android AppCompat library.  We updated our Android Library build.gradle file to include this dependency, and suddenly we had linked resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.appcompat:appcompat:1.1.0'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It turns out that when library modules are packaged up their Android res directory is combined into one values.xml file, including colors, dimensions, etc. In addition, they are linked with the R.txt file generated in the build so they can be referenced by consuming applications. This resource linking using R.txt is done by the Android AppCompat lib.&lt;/p&gt;

&lt;h2&gt;
  
  
  [Update] Continuous Integration
&lt;/h2&gt;

&lt;p&gt;Part of our project goals included building this library on a build server using Jenkins. One of the hard-learned lessons of this endeavor was that in using our 'container' Android project to build our AAR files on a build server isn't the same as doing Android Studio builds or even local command line builds.&lt;/p&gt;

&lt;p&gt;Biggest takeaway was a stupid-simple misstep: make sure your build machine sets the right &lt;code&gt;ANDROID_HOME&lt;/code&gt; directory!&lt;/p&gt;

&lt;h2&gt;
  
  
  Android for a Reason
&lt;/h2&gt;

&lt;p&gt;In my research for solving this particular problem I came across a wide array of responses about sharing resources across multiple projects. Overall, the conclusions on Stack Overflow were pretty grim, with people mostly just explaining why shared Resources are hard in Android. &lt;/p&gt;

&lt;p&gt;What I've learned is that packaging a bundle of resources to share across apps is very doable, you just have to respect the structure of a typical Android project and understand a bit about how AAR libraries are built. The more you know 🌟!&lt;/p&gt;

</description>
      <category>android</category>
      <category>resources</category>
      <category>design</category>
      <category>gradle</category>
    </item>
    <item>
      <title>Intro to Client Side Git Hooks</title>
      <dc:creator>A.J. Kueterman</dc:creator>
      <pubDate>Mon, 12 Aug 2019 15:20:09 +0000</pubDate>
      <link>https://forem.com/robotsquidward/intro-to-client-side-git-hooks-24mn</link>
      <guid>https://forem.com/robotsquidward/intro-to-client-side-git-hooks-24mn</guid>
      <description>&lt;p&gt;Automation is everything when it comes to the process of deploying code. Even the smallest improvements over time can have a massive impact on the amount of manual processes you have to endure in building and shipping your software.&lt;/p&gt;

&lt;p&gt;There are a lot of places to inject automation in the process of writing, building, and deploying code. When writing code on a team and on increasingly large projects those challenges and opportunities only grow. Things like linting, automated tests, and GitHub checks help you reduce &lt;a href="https://landing.google.com/sre/sre-book/chapters/eliminating-toil/" rel="noopener noreferrer"&gt;toil&lt;/a&gt; when pushing, reviewing, merging, and delivering code.&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%2Fuser-images.githubusercontent.com%2F10855762%2F62872161-e1500e00-bcea-11e9-8490-0533a31fc589.png" class="article-body-image-wrapper"&gt;&lt;img alt="Screen Shot 2019-08-12 at 10 20 40 AM" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F10855762%2F62872161-e1500e00-bcea-11e9-8490-0533a31fc589.png" width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are many solutions to these problems, and most focus on the most clear gatekeeper when it comes to deploying code - the build step. Your build should be able to detect problems automatically without human intervention. Everything from code style to performance to server health can be tested and certified in a build before you ever push code to production. The build enforces your rules and keeps developers honest in a way manual intervention never will be able to.&lt;/p&gt;

&lt;p&gt;However, there are checks that can save even more time when pushed forward into the pre-build process.&lt;/p&gt;

&lt;p&gt;A good example is developers committing and pushing bad code. Not &lt;em&gt;merging&lt;/em&gt; bad code, but just simply writing bad code and commiting it to source control in their own feature branches. This might not seem like a huge problem - after all protected branches mean you can enforce correctness with build checks before approving merges - but it can introduce manual effort and cause frustration for developers, both of which are things that could easily be avoided with client-side git protection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Git Hooks
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks" rel="noopener noreferrer"&gt;Git Hooks&lt;/a&gt; are a way to run scripts when certain Git commands are run. There are hooks on both client-side Git processes and server-side processes where you can react to changes in the Git workflow. We'll focus on the client-side hooks, where we can protect ourselves before we're even done with a commit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client-side Git Hooks
&lt;/h3&gt;

&lt;p&gt;The client-side hooks include hooks for multiple Git processes including committing, email workflows, and other processes like &lt;code&gt;checkout&lt;/code&gt; and &lt;code&gt;merge&lt;/code&gt;.  For now, let's focus on the commit workflow &lt;em&gt;(but &lt;a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks" rel="noopener noreferrer"&gt;check out the rest of the list&lt;/a&gt;, there's a lot you can do!)&lt;/em&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pre-commit&lt;/code&gt; - runs on &lt;code&gt;git commit&lt;/code&gt; even before the commit message is generated&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prepare-commit-msg&lt;/code&gt; - runs before the commit message editor opens but after the default message is generated&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;commit-msg&lt;/code&gt; - runs after message creation, to validate the temporary file containing the commit message&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These client-side &lt;code&gt;commit&lt;/code&gt; hooks really fall into two categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The code in your commit&lt;/li&gt;
&lt;li&gt;Formatting/enforcing a proper commit message&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We'll start with the first category, using &lt;code&gt;pre-commit&lt;/code&gt; to analyze the code we're about to commit before we even consider completing the commit with a message.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing a Git &lt;code&gt;pre-commit&lt;/code&gt; hook
&lt;/h3&gt;

&lt;p&gt;First, you need to designate a location for your hooks in your repository.  For our example, we're going to create a directory called &lt;code&gt;.githooks&lt;/code&gt; in the root of our repository using these commands:&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;mkdir&lt;/span&gt; .githooks
&lt;span class="nb"&gt;cd&lt;/span&gt; .githooks
&lt;span class="nb"&gt;touch &lt;/span&gt;areYouSure.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open up &lt;code&gt;areYouSure.sh&lt;/code&gt; in your favorite editor. Writing a hook is both extremely simple and overwhelmingly open-ended. You just need a script that returns an exit code, 0 for success and non-0 for failure. Anything that isn't 0 aborts the commit altogether.&lt;/p&gt;

&lt;p&gt;Let's write a very simple script that just asks our developer if they're sure they want to commit.&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="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;# Check that we want to commit.&lt;/span&gt;

&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Are you sure you want to commit this (y/n)? "&lt;/span&gt; answer
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;answer&lt;/span&gt;:0:1&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;in
    &lt;/span&gt;y|Y &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;0 &lt;span class="c"&gt;# If yes, success!&lt;/span&gt;
    &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1 &lt;span class="c"&gt;# If no, sorry yo.&lt;/span&gt;
    &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, make sure you make this new file executable by running&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;chmod&lt;/span&gt; +x .githooks/areYouSure.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's figure out how we can have Git actually run this script automatically as part of the &lt;code&gt;pre-commit&lt;/code&gt; hook.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a custom &lt;code&gt;pre-commit&lt;/code&gt; step
&lt;/h3&gt;

&lt;p&gt;In our &lt;code&gt;.githooks&lt;/code&gt; directory we'll create a new script to handle our custom &lt;code&gt;pre-commit&lt;/code&gt; actions with these commands:&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;cd&lt;/span&gt; .githooks
&lt;span class="nb"&gt;touch &lt;/span&gt;pre-commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the new &lt;code&gt;pre-commit&lt;/code&gt; file in your favorite editor. Let's update it to do the basic things we need.&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="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="c"&gt;# Specify the directory for the hooks.&lt;/span&gt;
&lt;span class="c"&gt;# We'll use the current one (.githooks)&lt;/span&gt;
&lt;span class="nv"&gt;hookDir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Specify the hooks you want to run during&lt;/span&gt;
&lt;span class="c"&gt;# the pre-commit process:&lt;/span&gt;
&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$hookDir&lt;/span&gt;&lt;span class="s2"&gt;/areYouSure.sh"&lt;/span&gt;
&lt;span class="c"&gt;# &amp;amp;&amp;amp; "hookDir/add-your-own-scripts-here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script will specify the directory where we'll store our hooks (the current &lt;code&gt;.githooks&lt;/code&gt; directory) and then it will specify the scripts that should run as part of &lt;code&gt;pre-commit&lt;/code&gt;. In our example we just have the single &lt;code&gt;areYouSure.sh&lt;/code&gt; script.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You could code your script code directly into &lt;code&gt;pre-commit&lt;/code&gt;. We are instead abstracting this code into its own script to enhance readability, reusability, and to enable us to easily add more scripts to our &lt;code&gt;pre-commit&lt;/code&gt; step in the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, make sure you make this new file executable by running&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;chmod&lt;/span&gt; +x .githooks/pre-commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tell Git to use our custom hooks
&lt;/h3&gt;

&lt;p&gt;After you've completed the steps above, you should have a new &lt;code&gt;.githooks&lt;/code&gt; directory containing the &lt;code&gt;pre-commit&lt;/code&gt; script and our new custom script &lt;code&gt;areYouSure.sh&lt;/code&gt;. We can go ahead and commit these files to source control. If you do this now, you'll notice that you don't get our 'are you sure' popup in the command line.&lt;/p&gt;

&lt;p&gt;The reason our hooks aren't run, is we still need to configure Git to let it know we have a new location for &lt;code&gt;pre-commit&lt;/code&gt; for this repository.  To do that, run this command in the root of your repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config core.hooksPath .githooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Git to configure the hooks path for this repo to our custom &lt;code&gt;.githooks&lt;/code&gt; directory. Now, whenever git runs the &lt;code&gt;pre-commit&lt;/code&gt; step it will defer to our custom actions in our own &lt;code&gt;pre-commit&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;Now, when you start a new &lt;code&gt;git commit&lt;/code&gt;, you should see our &lt;code&gt;areYouSure.sh&lt;/code&gt; prompt. A &lt;code&gt;Yes&lt;/code&gt; answer should continue your commit (moving on to commit message steps) while a &lt;code&gt;No&lt;/code&gt; answer should abort your commit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next Steps
&lt;/h3&gt;

&lt;p&gt;Congrats! 🎉  You have successfully written and configured a custom &lt;code&gt;pre-commit&lt;/code&gt; hook in your repository!&lt;/p&gt;

&lt;p&gt;Now, you can continue to improve your &lt;code&gt;pre-commit&lt;/code&gt; workflow by simply adding new scripts to &lt;code&gt;.githooks&lt;/code&gt; and specifying which hooks you want to run in your custom &lt;code&gt;pre-commit&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;A few resources to help continue your Git Hooks journey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://githooks.com" rel="noopener noreferrer"&gt;GitHooks.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pre-commit.com" rel="noopener noreferrer"&gt;pre-commit&lt;/a&gt; - a framework for pulling custom &lt;code&gt;pre-commit&lt;/code&gt; scripts from remote repos&lt;/li&gt;
&lt;li&gt;Learn about &lt;a href="https://git-scm.com/book/en/v2/Customizing-Git-An-Example-Git-Enforced-Policy#_an_example_git_enforced_policy" rel="noopener noreferrer"&gt;server-side git hooks to enforce policy&lt;/a&gt; and some starter &lt;a href="https://github.com/github/platform-samples/tree/master/pre-receive-hooks" rel="noopener noreferrer"&gt;server-side scripts&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading, if you have questions or thoughts on &lt;code&gt;pre-commit&lt;/code&gt; hooks or want to share the awesome stuff you've done with git automation - let me know in the comments!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;I originally published this post on AJ.dev, &lt;a href="https://ajkueterman.dev/posts/intro-to-custom-client-git-hooks-pre-commit/" rel="noopener noreferrer"&gt;read it there&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>git</category>
      <category>bash</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Your First Android Libraries</title>
      <dc:creator>A.J. Kueterman</dc:creator>
      <pubDate>Mon, 22 Jul 2019 17:37:04 +0000</pubDate>
      <link>https://forem.com/robotsquidward/your-first-android-libraries-47k9</link>
      <guid>https://forem.com/robotsquidward/your-first-android-libraries-47k9</guid>
      <description>&lt;p&gt;When you start a brand new app in Android Studio, you're dropped into a bare-bones project that likely just has a single, empty Activity.&lt;/p&gt;

&lt;p&gt;If you're trying to build out a project beyond "&lt;code&gt;Hello World!&lt;/code&gt;", especially if you plan to follow some architectural best practices like using MVVM, you'll quickly run into some limitations with the basic Android libraries included in every project.&lt;/p&gt;

&lt;p&gt;After creating a few basic starter applications, I thought I'd share what I think are the best "first step" dependencies to add to your Android project.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR - &lt;code&gt;app/build.gradle&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Before I go into more detail, here's the basic &lt;code&gt;dependencies&lt;/code&gt; list I put together for my last starter app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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="n"&gt;implementation&lt;/span&gt; &lt;span class="nf"&gt;fileTree&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;dir:&lt;/span&gt; &lt;span class="s1"&gt;'libs'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;include:&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'*.jar'&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;"org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.appcompat:appcompat:1.0.2'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.core:core-ktx:1.0.2'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.constraintlayout:constraintlayout:1.1.3'&lt;/span&gt;
    &lt;span class="c1"&gt;// enable ViewModelProviders&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"androidx.lifecycle:lifecycle-extensions:2.0.0"&lt;/span&gt;
    &lt;span class="c1"&gt;// enable TextInputLayout&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'com.google.android.material:material:1.0.0'&lt;/span&gt;

    &lt;span class="n"&gt;testImplementation&lt;/span&gt; &lt;span class="s1"&gt;'junit:junit:4.12'&lt;/span&gt;
    &lt;span class="c1"&gt;// enable Mocking with Mockito&lt;/span&gt;
    &lt;span class="n"&gt;testImplementation&lt;/span&gt; &lt;span class="s1"&gt;'org.mockito:mockito-core:2.19.0'&lt;/span&gt;
    &lt;span class="c1"&gt;// enable InstantTaskExecutorRule&lt;/span&gt;
    &lt;span class="n"&gt;testImplementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.arch.core:core-testing:2.0.1'&lt;/span&gt;

    &lt;span class="n"&gt;androidTestImplementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.test:runner:1.2.0'&lt;/span&gt;
    &lt;span class="n"&gt;androidTestImplementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.test.espresso:espresso-core:3.2.0'&lt;/span&gt;
    &lt;span class="c1"&gt;// non-deprecated AndroidJUnit4&lt;/span&gt;
    &lt;span class="n"&gt;androidTestImplementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.test.ext:junit:1.1.1'&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lines I commented are the ones I added to what was included with the basic Android blank-Activity starter app in Android Studio 3.4. &lt;/p&gt;

&lt;p&gt;Let's take a closer look at what these libraries are and why we need them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prod Code
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;implementation&lt;/code&gt; keyword specifies dependencies in your actual, running, production code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lifecycle Extensions
&lt;/h3&gt;

&lt;p&gt;Enable some handy extensions for Android &lt;a href="https://developer.android.com/topic/libraries/architecture/lifecycle" rel="noopener noreferrer"&gt;Lifecycle components&lt;/a&gt;. Namely, the ability to use &lt;a href="https://developer.android.com/reference/android/arch/lifecycle/ViewModelProviders" rel="noopener noreferrer"&gt;&lt;code&gt;ViewModelProviders&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"androidx.lifecycle:lifecycle-extensions:2.0.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any Android project needs an architectural pattern to follow. &lt;a href="https://developer.android.com/jetpack/docs/guide" rel="noopener noreferrer"&gt;Google recommends using MVVM&lt;/a&gt; with the Android &lt;a href="https://developer.android.com/topic/libraries/architecture/viewmodel" rel="noopener noreferrer"&gt;ViewModel&lt;/a&gt;. &lt;code&gt;ViewModelProviders&lt;/code&gt; enables you to quickly setup &lt;code&gt;ViewModel&lt;/code&gt;s in your Activities and Fragments in a Google-approved way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Material
&lt;/h3&gt;

&lt;p&gt;You can get by without many of the &lt;a href="https://material.io/develop/android/" rel="noopener noreferrer"&gt;Material Design components&lt;/a&gt;, but it's easier to use them. They support some very useful UI elements that Android users are used to seeing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'com.google.android.material:material:1.0.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For me, this library allowed me to use &lt;a href="https://developer.android.com/reference/com/google/android/material/textfield/TextInputLayout" rel="noopener noreferrer"&gt;&lt;code&gt;TextInputLayout&lt;/code&gt;&lt;/a&gt; for my text fields. This enables built in error fields, animated hints, and a Material-first design that I wanted in my app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unit Tests
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;testImplementation&lt;/code&gt; specifies dependencies for your Unit Tests. JUnit is included by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mockito
&lt;/h3&gt;

&lt;p&gt;The included &lt;code&gt;ExampleUnitTest&lt;/code&gt; in any Android beginner project doesn't use &lt;a href="https://site.mockito.org/" rel="noopener noreferrer"&gt;Mockito&lt;/a&gt; to test. However, as projects grow, you'll quickly realize the benefits of a full-fledged mocking library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;testImplementation&lt;/span&gt; &lt;span class="s1"&gt;'org.mockito:mockito-core:2.19.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mockito enables you to use the &lt;code&gt;MockitoJUnitRunner&lt;/code&gt; and to mock data and function calls in your test. It really helps to reduce boiler plate and focus on the right functionality in your unit tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lifecycle Testing
&lt;/h3&gt;

&lt;p&gt;If you're doing the right thing and following MVVM with the Android Architecture Components, then you'll probably want to test your &lt;code&gt;ViewModel&lt;/code&gt;s and &lt;code&gt;LiveData&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;testImplementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.arch.core:core-testing:2.0.1'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core-testing library enables you to test &lt;code&gt;LiveData&lt;/code&gt; in your JUnit tests using the &lt;code&gt;InstrumentationTestRegistry&lt;/code&gt; rule.&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="cm"&gt;/**
 * Enables Architecture Components testing in JUnit tests.
 * (Include this at the top of your Unit Test class body.)
 */&lt;/span&gt;
&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;Rule&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;task&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InstantTaskExecutorRule&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The &lt;code&gt;androidTestImplementation&lt;/code&gt; keyword refers to UI/Instrumentation test dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Android JUnit
&lt;/h3&gt;

&lt;p&gt;While you don't need this to use &lt;code&gt;AndroidJUnit4&lt;/code&gt;, this dependency includes the latest, non-deprecated version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;androidTestImplementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.test.ext:junit:1.1.1'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;Hopefully this guide has helped you identify some foundational dependencies you'll need for almost any Android project.&lt;/p&gt;

&lt;p&gt;It's impossible to be prescriptive about what dependencies you'll need to explore the infinite possibilities available to you in your next Android project.  What's next is up to you.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;This article is republished from my blog, &lt;a href="https://ajkueterman.dev" rel="noopener noreferrer"&gt;AJ.dev&lt;/a&gt; - &lt;a href="https://ajkueterman.dev/posts/your-first-android-libraries/" rel="noopener noreferrer"&gt;read it there&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>java</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Quick Guide to ASWebAuthenticationSession API Changes in iOS 13</title>
      <dc:creator>A.J. Kueterman</dc:creator>
      <pubDate>Wed, 17 Jul 2019 21:34:13 +0000</pubDate>
      <link>https://forem.com/robotsquidward/quick-guide-to-aswebauthenticationsession-api-changes-in-ios-13-4m8i</link>
      <guid>https://forem.com/robotsquidward/quick-guide-to-aswebauthenticationsession-api-changes-in-ios-13-4m8i</guid>
      <description>&lt;p&gt;One of the biggest announcements at WWDC 2019 was the new '&lt;a href="https://developer.apple.com/sign-in-with-apple/" rel="noopener noreferrer"&gt;Sign In With Apple&lt;/a&gt;' feature, where Apple will now provide an authentication email &amp;amp; password to apps on behalf of you and manage them securely using the iCloud Keychain. &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%2Fbp9n2rxyg3zis5ulm9bk.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%2Fbp9n2rxyg3zis5ulm9bk.png" alt="sign-in-with-apple" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apple continues to push these privacy-focused features especially around authentication to try to disrupt ubiquitous services like Facebook &amp;amp; Google OAuth that make login flows much easier for users - but at the cost of their data going into the hands of ad companies.&lt;/p&gt;

&lt;p&gt;As such, a lot of focus has been given to the Authentication services in iOS. We saw it begin last year with the move from auth services inside &lt;a href="https://developer.apple.com/documentation/safariservices" rel="noopener noreferrer"&gt;&lt;code&gt;SafariServices&lt;/code&gt;&lt;/a&gt; to the dedicated &lt;a href="https://developer.apple.com/documentation/authenticationservices" rel="noopener noreferrer"&gt;&lt;code&gt;AuthenticationServices&lt;/code&gt;&lt;/a&gt; APIs. This year, iOS 13 leverages &lt;code&gt;AuthenticationServices&lt;/code&gt; to enable the Sign In With Apple APIs, and continue to refine the sign in experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Guide to Changes in &lt;a href="https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession" rel="noopener noreferrer"&gt;&lt;code&gt;ASWebAuthenticationSession&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Last year, I talked a bit about OAuth in iOS in &lt;a href="https://ajkueterman.dev/posts/sfauthenticationsession-and-aswebauthenticationsession/" rel="noopener noreferrer"&gt;my post about Apple's move from &lt;code&gt;SFAuthenticationSession&lt;/code&gt; to &lt;code&gt;ASWebAuthenticationSession&lt;/code&gt;&lt;/a&gt; and how I went about converting my iOS app to use the new API. This year, the &lt;code&gt;ASWebAuthenticationSession&lt;/code&gt; has some smaller tweaks to enhance the OAuth sign-in experience across iOS (including iPadOS) devices.&lt;/p&gt;

&lt;p&gt;If you're a developer already sweating about the huge array of changes and opportunities coming for developers in 2019, don't fret about this one, it's a small tweak purely for enhancing OAuth experiences across devices.&lt;/p&gt;

&lt;p&gt;To set the stage, let's look at a code snippet for &lt;code&gt;ASWebAuthenticationSession&lt;/code&gt; in iOS 12.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;webAuthSession&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ASWebAuthenticationSession&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="kd"&gt;@available&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iOS&lt;/span&gt; &lt;span class="mf"&gt;12.0&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="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;getAuthTokenWithWebLogin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;authURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://github.com/login/oauth/authorize?client_id=&amp;lt;client_id&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="nv"&gt;callbackUrlScheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"octonotes://auth"&lt;/span&gt;

    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webAuthSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ASWebAuthenticationSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;authURL&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;callbackURLScheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;callbackUrlScheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;completionHandler&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="nv"&gt;callBack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;

        &lt;span class="c1"&gt;// handle auth response&lt;/span&gt;
        &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;successURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;callBack&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;oauthToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSURLComponents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;successURL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;absoluteString&lt;/span&gt;&lt;span class="p"&gt;))?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queryItems&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;

        &lt;span class="c1"&gt;// Do what you now that you've got the token, or use the callBack URL&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;oauthToken&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"No OAuth Token"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;// Kick it off&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webAuthSession&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the only thing we need to provide the &lt;code&gt;ASWebAuthenticationSession&lt;/code&gt; is the authentication &lt;code&gt;URL&lt;/code&gt;, a callback URL scheme, and a completion block that handles the result of the OAuth. The OS handles the rest - displaying an alert, launching a Web login flow, and dismissing.&lt;/p&gt;

&lt;p&gt;In iOS 13, as Apple continues to refine the multi-app experience for iOS and iPadOS, we now need to help the OS out when it's making the decision on where and how to display the OAuth Alert and Web login flow.&lt;/p&gt;

&lt;p&gt;To do that, we have to let the &lt;code&gt;ASWebAuthenticationSession&lt;/code&gt; know which window is presenting the OAuth request. This is done by implementing the &lt;a href="https://developer.apple.com/documentation/authenticationservices/aswebauthenticationpresentationcontextproviding" rel="noopener noreferrer"&gt;&lt;code&gt;ASWebAuthenticationPresentationContextProviding&lt;/code&gt;&lt;/a&gt; interface in your presenting View Controller. &lt;/p&gt;

&lt;p&gt;The presenting View Controller needs to implement the &lt;code&gt;ASWebAuthenticationPresentationContextProviding&lt;/code&gt; interface and return the relevant window in the &lt;code&gt;presentationAnchor&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;LoginViewController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIViewController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ASWebAuthenticationPresentationContextProviding&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;presentationAnchor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ASWebAuthenticationSession&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;ASPresentationAnchor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="kt"&gt;ASPresentationAnchor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, when setting up our auth session, we need to specify our &lt;a href="https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession/3237232-presentationcontextprovider" rel="noopener noreferrer"&gt;&lt;code&gt;presentationContextProvider&lt;/code&gt;&lt;/a&gt; delegate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webAuthSession&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;presentationContextProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full updated method, now passing in a &lt;code&gt;ASWebAuthenticationPresentationContextProviding&lt;/code&gt; context (our presenting VC that implements the &lt;code&gt;ASWebAuthenticationPresentationContextProviding&lt;/code&gt; interface).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;webAuthSession&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ASWebAuthenticationSession&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="kd"&gt;@available&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iOS&lt;/span&gt; &lt;span class="mf"&gt;13.0&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="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;getAuthTokenWithWebLogin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ASWebAuthenticationPresentationContextProviding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;authURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://github.com/login/oauth/authorize?client_id=&amp;lt;client_id&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="nv"&gt;callbackUrlScheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"octonotes://auth"&lt;/span&gt;

    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webAuthSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ASWebAuthenticationSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;authURL&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;callbackURLScheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;callbackUrlScheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;completionHandler&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="nv"&gt;callBack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;

        &lt;span class="c1"&gt;// handle auth response&lt;/span&gt;
        &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;successURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;callBack&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;oauthToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSURLComponents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;successURL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;absoluteString&lt;/span&gt;&lt;span class="p"&gt;))?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queryItems&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;

        &lt;span class="c1"&gt;// Do what you now that you've got the token, or use the callBack URL&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;oauthToken&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"No OAuth Token"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;// New in iOS 13&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webAuthSession&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;presentationContextProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;

    &lt;span class="c1"&gt;// Kick it off&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webAuthSession&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&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;At this point, your &lt;code&gt;ASWebAuthenticationSession&lt;/code&gt; now knows where and how to display your Web-based login flow, and will provide a consistent experience across devices!&lt;/p&gt;

&lt;h2&gt;
  
  
  Thoughts on OAuth
&lt;/h2&gt;

&lt;p&gt;It has been an interesting experience tinkering with OAuth over the last two years. Right as I was learning the APIs Apple made a big move to new APIs, and this year it's clear how that change is enabling a better sign in experience for users. &lt;/p&gt;

&lt;p&gt;Now, in iOS 13, by observing the specific change in this small API, we can see the evolution of iOS to a multi-window experience.&lt;/p&gt;

&lt;p&gt;As I see this slow progression play out, I'm beginning to wonder about the future of OAuth in iOS at all. Will web authentication be blacklisted altogether in favor of Sign In With Apple? Will Apple centralize auth in a way that allows me to authenticate with GitHub without going to GitHub services for tokens? Only time will tell, but the changes today can project the changes in the future - stay alert!&lt;/p&gt;

&lt;p&gt;If you have any thoughts/questions/predictions about OAuth and Web Authentication with iOS, don't hesitate to reach out to me on Twitter &lt;a href="https://www.linkedin.com/in/ajkueterman/" rel="noopener noreferrer"&gt;@ajkueterman&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swift</category>
      <category>oauth</category>
      <category>wwdc</category>
    </item>
  </channel>
</rss>
