<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jared Hall</title>
    <description>The latest articles on DEV Community by Jared Hall (@jaredhall).</description>
    <link>https://dev.to/jaredhall</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%2F3840805%2F8caebb0a-6ec8-4dc8-8f5c-e7f8cbd8ba49.jpeg</url>
      <title>DEV Community: Jared Hall</title>
      <link>https://dev.to/jaredhall</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jaredhall"/>
    <language>en</language>
    <item>
      <title>I Keep Forgetting About My Long-Running Mobile App Builds</title>
      <dc:creator>Jared Hall</dc:creator>
      <pubDate>Tue, 24 Mar 2026 14:12:06 +0000</pubDate>
      <link>https://dev.to/jaredhall/i-keep-forgetting-about-my-long-running-mobile-app-builds-343i</link>
      <guid>https://dev.to/jaredhall/i-keep-forgetting-about-my-long-running-mobile-app-builds-343i</guid>
      <description>&lt;p&gt;I'm a mobile developer. My Android builds take me easily 15-20 minutes. iOS with Fastlane, tests, code signing, and TestFlight upload? Easily 30 minutes.&lt;/p&gt;

&lt;p&gt;Every time I make a PR, I'd start a build, switch to something else, and forget about it. An hour later, I'd come back and check the Actions tab to see that the build failed 45 minutes ago. Or that the build actually worked and QA has been waiting on this for a long time.&lt;/p&gt;

&lt;p&gt;This has been happening too many times for me to count, so I built something to solve this problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;There's no way to automatically sense that the build is complete. You just need to keep an eye on the Actions tab or wait for your PR to become mergeable.&lt;/p&gt;

&lt;p&gt;What I actually wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A push notification on my phone the moment the build finishes&lt;/li&gt;
&lt;li&gt;A direct link that I could tap the notification and go straight to Firebase App Distribution or TestFlight&lt;/li&gt;
&lt;li&gt;Different messages for success and failure&lt;/li&gt;
&lt;li&gt;A link to the GitHub Actions run for failed builds so that I can see what went wrong&lt;/li&gt;
&lt;li&gt;No waiting around, no checking every 10 minutes and no post-it notes under the monitor&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I did
&lt;/h2&gt;

&lt;p&gt;I created a simple service that sends push notifications to your phone. You send it an event via the API or GitHub Actions, and it buzzes your phone. The key feature for CI/CD pipelines is that it can include a link in the notification. You can tap the notification to get to where you need to go. You can find the service at &lt;a href="https://apialerts.com" rel="noopener noreferrer"&gt;API Alerts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's what my Android staging workflow looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload to Firebase&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wzieba/Firebase-Distribution-Github-Action@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ANDROID_FIREBASE_APP_ID }}&lt;/span&gt;
    &lt;span class="na"&gt;serviceCredentialsFileContent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GCP_CREDENTIALS }}&lt;/span&gt;
    &lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;staging&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app/build/outputs/apk/release/app-release.apk&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Notify&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;success() || failure()&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apialerts/notify-action@v2&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.API_ALERTS_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;developer'&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.status == 'success' &amp;amp;&amp;amp; '🚀 Android staging deployed' || '❌ Android staging failed' }}&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy,staging,android'&lt;/span&gt;
    &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.status == 'success' &amp;amp;&amp;amp; 'https://appdistribution.firebase.google.com/testerapps/MY_APP_ID' || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;if: success() || failure()&lt;/code&gt; is important. It sends a notification no matter the outcome but uses different messages&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow now
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Push code to a PR&lt;/li&gt;
&lt;li&gt;Context switch. Make a coffee, quick power nap, play with my cat Earl, review another PR, whatever&lt;/li&gt;
&lt;li&gt;Phone buzzes: "🚀 Android staging deployed"&lt;/li&gt;
&lt;li&gt;Tap → Firebase App Distribution opens → install → test&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Phone buzzes: "❌ Android staging failed"&lt;/li&gt;
&lt;li&gt;Tap → The GitHub Actions run opens → see exactly what broke&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The entire process from "push code" to "testing the build on my phone" now requires zero attention&lt;/p&gt;

&lt;h2&gt;
  
  
  iOS too
&lt;/h2&gt;

&lt;p&gt;I use the same approach for iOS builds. My Fastlane workflow uploads to Firebase App Distribution (or TestFlight) and then sends the notification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test App&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fastlane ios tests&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fastlane ios build_beta&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fastlane ios deploy_beta group:staging&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Notify&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;success() || failure()&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apialerts/notify-action@v2&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.API_ALERTS_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;developer'&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.status == 'success' &amp;amp;&amp;amp; '🚀 iOS staging deployed' || '❌ iOS staging failed' }}&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy,staging,ios'&lt;/span&gt;
    &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.status == 'success' &amp;amp;&amp;amp; 'https://appdistribution.firebase.google.com/testerapps/MY_APP_ID' || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Staging vs production
&lt;/h2&gt;

&lt;p&gt;I use separate channels to keep things organised:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;developer&lt;/code&gt;&lt;/strong&gt; for staging and feature branch builds. Just me.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;releases&lt;/code&gt;&lt;/strong&gt; for production builds. The messages are different too:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Production&lt;/span&gt;
&lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;releases'&lt;/span&gt;
&lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.status == 'success' &amp;amp;&amp;amp; '🚢 Android production ready to submit' || '❌ Android production failed' }}&lt;/span&gt;
&lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.status == 'success' &amp;amp;&amp;amp; 'https://play.google.com/store/apps/details?id=com.myapp' || '...' }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  It's not just mobile
&lt;/h2&gt;

&lt;p&gt;I’ve taken this approach and applied it to my backend deployments, web builds, and even SDK releases. Any build or deployment that takes more than a minute and may fail now sends a notification.&lt;/p&gt;

&lt;p&gt;The GitHub action is a simple step to add to any workflow. If you’re a mobile dev spending your day waiting on CI, &lt;a href="https://github.com/marketplace/actions/api-alerts-github-action-notify" rel="noopener noreferrer"&gt;try adding this action&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I wrote a more detailed step-by-step &lt;a href="https://apialerts.com/blog/mobile-ci-cd-build-alerts" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; on the API Alerts blog if you want the full walkthrough.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm the developer behind &lt;a href="https://apialerts.com" rel="noopener noreferrer"&gt;API Alerts&lt;/a&gt;, I built it because I needed it. If you have questions about the setup or want to see other use cases, drop a comment.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>mobile</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
