<?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: Cathy Lai</title>
    <description>The latest articles on Forem by Cathy Lai (@cathylai).</description>
    <link>https://forem.com/cathylai</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%2F904121%2Fdeac3704-41b3-4e1b-a391-0257ed0e729f.jpeg</url>
      <title>Forem: Cathy Lai</title>
      <link>https://forem.com/cathylai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/cathylai"/>
    <language>en</language>
    <item>
      <title>A New Way to Look at “Failures”</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Mon, 30 Mar 2026 09:57:08 +0000</pubDate>
      <link>https://forem.com/cathylai/a-new-way-to-look-at-failures-1i9o</link>
      <guid>https://forem.com/cathylai/a-new-way-to-look-at-failures-1i9o</guid>
      <description>&lt;h3&gt;
  
  
  Pursuing Correctness as Developers
&lt;/h3&gt;

&lt;p&gt;As developers, we are trained from school to prioritize correctness. We focus on logic, clean architecture, and passing tests because, in code, being "right" leads to success while being "wrong" leads to failure. Over time, this conditioning causes us to tie our self-worth to being correct, which can become a major hurdle when we transition into building products for the real world.&lt;/p&gt;

&lt;p&gt;When we start creating content or apps, the predictable link between effort and results often disappears. We might build something technically perfect that nobody uses, or write something insightful that nobody reads. This feels like a failure in the traditional sense, but &lt;strong&gt;in the market, "errors" aren't signs of being wrong—they are simply data points.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  A World with no Predefinded Correctness
&lt;/h3&gt;

&lt;p&gt;High-performing developers often fall into a trap where they view a lack of traction as a personal flaw. In programming, a bug is a mistake to be fixed, but in entrepreneurship, being wrong is the primary way you learn. &lt;strong&gt;It is important to shift our mindset from "I failed" to "That experiment didn't land,"&lt;/strong&gt; recognizing that frequent failure is a necessary part of the process.&lt;/p&gt;

&lt;p&gt;This requires a new definition of confidence. True confidence isn't the belief that you will always succeed; it’s being okay regardless of the outcome. While developers are used to seeing output as validation, we must learn to see it as an experiment. You can care deeply about the work without letting the market's reaction judge your personal worth.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Billion Dollar Product Emerges from Failures
&lt;/h3&gt;

&lt;p&gt;A classic example of this is the origin of Twitch. Justin Kan initially launched Justin.tv to stream his life 24/7, which didn't succeed as a mainstream concept. However, instead of taking it as a personal failure, he noticed users were interested in the streaming technology itself—specifically for gaming. By viewing the "failed" project as &lt;strong&gt;market feedback&lt;/strong&gt; &lt;strong&gt;rather than a dead end,&lt;/strong&gt; he was able to pivot into a billion-dollar company.&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%2Fqn7lbrdqayszshxgsv77.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%2Fqn7lbrdqayszshxgsv77.png" alt=" " width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mindset Shift
&lt;/h3&gt;

&lt;p&gt;Ultimately, we must undergo an identity shift: we are no longer someone who proves value by being right, but someone who creates value by running experiments. This means shipping imperfect things and accepting that we will be ignored or wrong often. By launching/publishing often and observing, we turn failures into data points that fuel long-term growth.&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>todayilearned</category>
      <category>developers</category>
      <category>mentalhealth</category>
    </item>
    <item>
      <title>Why Can’t Apple Just Accept a Pull Request for My iOS App? (Top Over-The-Air Update Questions Answered:)</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Tue, 24 Mar 2026 18:57:50 +0000</pubDate>
      <link>https://forem.com/cathylai/why-cant-apple-just-accept-a-pull-request-for-my-ios-app-top-over-the-air-update-questions-39oo</link>
      <guid>https://forem.com/cathylai/why-cant-apple-just-accept-a-pull-request-for-my-ios-app-top-over-the-air-update-questions-39oo</guid>
      <description>&lt;p&gt;&lt;em&gt;App Store, EAS Builds, OTA Updates — Explained&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Over the Air Update - Concept
&lt;/h2&gt;

&lt;p&gt;OTA allows us to update our app &lt;strong&gt;without rebuilding and resubmitting a new version to the App Store or Play Store&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The app fetches updated code/content from a server&lt;/li&gt;
&lt;li&gt;The update will be applied on launch (or in the background)&lt;/li&gt;
&lt;li&gt;Users won't need to click "Update".&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Questions &amp;amp; Answers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ❓ “Why can’t I just deploy the updates like Vercel?”
&lt;/h3&gt;

&lt;p&gt;Because mobile apps don’t run on the servers. &lt;/p&gt;

&lt;p&gt;On the web:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code lives on the server&lt;/li&gt;
&lt;li&gt;Users always get the latest version&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On mobile:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code is compiled into a &lt;strong&gt;binary&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;It lives on the user’s phone&lt;/li&gt;
&lt;li&gt;Users may stay on old versions forever&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❓ “Why does Apple/Google need to review my app?”
&lt;/h3&gt;

&lt;p&gt;Because an app runs on the user’s device and can access: camera, photos, contacts &amp;amp; payments. Apple/Google isn’t reviewing the code like a PR. They’re reviewing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Is this app safe and trustworthy for users?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  ❓ “Why not just review the code like GitHub?”
&lt;/h3&gt;

&lt;p&gt;Because code doesn’t fully represent behaviour. An app can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetch remote data&lt;/li&gt;
&lt;li&gt;Enable features dynamically&lt;/li&gt;
&lt;li&gt;Change UI based on config&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Apple/Google reviews &lt;strong&gt;what users experience&lt;/strong&gt;, not just static code.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❓ “Why is mobile deployment so slow?”
&lt;/h3&gt;

&lt;p&gt;Because mistakes are expensive.&lt;/p&gt;

&lt;p&gt;On web:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bug → redeploy → fixed instantly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On mobile:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bug → shipped to thousands of devices&lt;/li&gt;
&lt;li&gt;Some users never update&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Bugs are &lt;strong&gt;persistent&lt;/strong&gt;, not temporary&lt;/p&gt;

&lt;h3&gt;
  
  
  ❓ “So what problem does OTA (Over-The-Air updates) solve?”
&lt;/h3&gt;

&lt;p&gt;OTA lets us update parts of our app &lt;strong&gt;without resubmitting to the App Store&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rebuild → resubmit → wait&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;push update → users get it on next app open&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 It brings mobile a bit closer to web-like iteration speed&lt;/p&gt;

&lt;h3&gt;
  
  
  ❓ “What is the limitation of OTA?”
&lt;/h3&gt;

&lt;p&gt;OTA updates &lt;strong&gt;cannot change the native layer&lt;/strong&gt;. If we add a new native library (e.g., a new camera plugin or a Bluetooth library), we &lt;strong&gt;must&lt;/strong&gt; submit a new build to the App Store.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❓ “Does OTA bypass App Review?”
&lt;/h3&gt;

&lt;p&gt;No.&lt;/p&gt;

&lt;p&gt;OTA works &lt;strong&gt;within the boundaries of what Apple already approved&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;✅ Allowed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bug fixes&lt;/li&gt;
&lt;li&gt;UI tweaks&lt;/li&gt;
&lt;li&gt;Text/content changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❌ Not allowed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New core features&lt;/li&gt;
&lt;li&gt;Changing app purpose&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❓ “So Apple/Google still controls everything?”
&lt;/h3&gt;

&lt;p&gt;Yes — but selectively. Think of it like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;App Store = &lt;strong&gt;initial approval of your app’s behavior&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;OTA = &lt;strong&gt;small adjustments within that approved scope&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❓ “Why does OTA feel limited?”
&lt;/h3&gt;

&lt;p&gt;Because it was added after the original design. Mobile was only supposed to be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“build → review → install”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OTA is a layer on top trying to make it more flexible — but the App Store still enforces control.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❓ “Why is Expo so popular for OTA?”
&lt;/h3&gt;

&lt;p&gt;Because it makes OTA simple. Instead of building our own system, Expo gives us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hosting&lt;/li&gt;
&lt;li&gt;Version control&lt;/li&gt;
&lt;li&gt;Compatibility checks&lt;/li&gt;
&lt;li&gt;Rollback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Using &lt;code&gt;eas update&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ❓ “How is this different from Flutter?”
&lt;/h3&gt;

&lt;p&gt;Flutter apps are compiled ahead of time. So instead of OTA:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They rely more on app store updates&lt;/li&gt;
&lt;li&gt;and use remote config / feature flags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Less “code updates”, more “data-driven updates”&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;br&gt;
&lt;em&gt;While true that Flutter doesn't have an official, first-party OTA solution like Expo Updates, third-party tools (like Shorebird) now exist to bring OTA-style patching to Flutter by modifying the underlying engine.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Comparison with Web's "React Server Component (RSC)"
&lt;/h2&gt;

&lt;p&gt;RSC in web is trying to solve a similar problem, i.e. let server has more control over the code, not the client.&lt;/p&gt;
&lt;h3&gt;
  
  
  🌐 Web (Next.js)
&lt;/h3&gt;

&lt;p&gt;→ &lt;strong&gt;React Server Components&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  UI generated on server&lt;/li&gt;
&lt;li&gt;  streamed to browser&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  📱 Mobile (React Native / Flutter)
&lt;/h3&gt;

&lt;p&gt;→ &lt;strong&gt;Server-driven UI + Feature Flags + OTA&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sends configs that the app maps to pre-built native components&lt;/li&gt;
&lt;li&gt;OTA for small fixes &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  💡 One-line takeaway
&lt;/h2&gt;

&lt;p&gt;Mobile isn’t slower than web —&lt;br&gt;
it’s optimized for &lt;strong&gt;trust and stability&lt;/strong&gt;, not instant change.&lt;/p&gt;
&lt;h2&gt;
  
  
  Curious about how Expo OTA Works?
&lt;/h2&gt;

&lt;p&gt;Please watch a short demo below where I added a small paragraph to my entry screen, pushed it the App Store, and confirmed it on a relaunch on my phone. &lt;/p&gt;

&lt;p&gt;Step by step notes from the video &lt;a href="https://dev.to/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-sending-updates-part-55-1gj2"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Sl7Y6f2oCnU?start=18"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>ota</category>
      <category>appstore</category>
      <category>reactnative</category>
      <category>mobile</category>
    </item>
    <item>
      <title>CI/CD for Mobile Apps Part 1/3 - Know Where Our Code Lives (Web vs Mobile Explained)</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Thu, 19 Mar 2026 17:41:06 +0000</pubDate>
      <link>https://forem.com/cathylai/cicd-for-mobile-apps-part-13-know-where-our-code-lives-web-vs-mobile-explained-59ah</link>
      <guid>https://forem.com/cathylai/cicd-for-mobile-apps-part-13-know-where-our-code-lives-web-vs-mobile-explained-59ah</guid>
      <description>&lt;p&gt;After a few days of working on CI/CD pipelines for mobile apps enabling OTA ("Over the Air" updates), I realised a few details that feels like just simple settings, but actually are resulted from a complete different underlying model which mobile apps operated under.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the Code Resides
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Web apps: The Single Source of Truth
&lt;/h3&gt;

&lt;p&gt;For web development, the code lives on the server, with the exception of browser cache where the code did not change from the last time. This means the newer code we pushed will always show up for the users, and every user will have the latest changes. &lt;/p&gt;

&lt;h3&gt;
  
  
  Mobile apps: Distributed Binaries
&lt;/h3&gt;

&lt;p&gt;Mobile apps, however, the code is compiled into binary and downloaded onto users' phones. The server just hosts the backend services like APIs, user management, and database queries and storage. &lt;/p&gt;

&lt;p&gt;When a new update is available (eg layout change, new camera features) the users must "download" it, else they stay on the old version. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Analogy
&lt;/h3&gt;

&lt;p&gt;So web apps are like &lt;strong&gt;electronic billboards&lt;/strong&gt;. There’s only one version, controlled by us. When we change it, every user sees the update instantly.&lt;/p&gt;

&lt;p&gt;Mobile apps, on the other hand, are like &lt;strong&gt;printed newsletters&lt;/strong&gt;. Every user holds their own copy. Some are on last month’s issue, some are on older ones. You can’t "remote delete" the paper in their hands; they have to go out and get the new edition. And if you send them content meant for a newer issue than what they have on hand... it might not make sense (crash).&lt;/p&gt;

&lt;h2&gt;
  
  
  The "runtime version" Config
&lt;/h2&gt;

&lt;p&gt;This is why we have something like this in our config&lt;br&gt;
app.json&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"expo"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"runtimeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="nl"&gt;"policy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"appVersion"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;---&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;app's&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;

      &lt;/span&gt;&lt;span class="nl"&gt;"slug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"recipes-collect"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

      &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;---&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;version&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is telling the update server how to determine which "issue" the user currently has.   &lt;/p&gt;

&lt;p&gt;There are also other ways to determine this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"policy"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sdkVersion"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;---&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Expo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;SDK&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;version&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"policy"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fingerprint"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;---&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;digital&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;signature&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;native&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;code&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So when we push an Over the Air (OTA) update, the app on a user's phone checks this configuration to decide: "Is this update compatible with the native code I have installed?"&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Diagram
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevype71u3582e6trs5m7.jpeg" 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%2Fevype71u3582e6trs5m7.jpeg" alt=" " width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having this in mind, we will design our pipelines differently from web apps. In Part 2 tomorrow, we will use "EAS Workflow" from Expo to write scripts to automate the deployment.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>mobile</category>
      <category>ota</category>
      <category>devops</category>
    </item>
    <item>
      <title>Challenge: iOS App from Zero to a Friend's Phone in a Different City in 1 Hour</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Wed, 11 Mar 2026 21:31:59 +0000</pubDate>
      <link>https://forem.com/cathylai/challenge-ios-app-from-zero-to-a-friends-phone-in-1-hour-4gfo</link>
      <guid>https://forem.com/cathylai/challenge-ios-app-from-zero-to-a-friends-phone-in-1-hour-4gfo</guid>
      <description>&lt;p&gt;I was browsing the Expo website and chanced upon their new product, &lt;strong&gt;EAS Launch&lt;/strong&gt;. It promises a 'one-click' launch to the App Store. &lt;/p&gt;

&lt;p&gt;I’ve long thought that someone should build a tool &lt;strong&gt;equivalent to Vercel or Netlify for mobile&lt;/strong&gt;, so I’m very curious to see if it delivers on that promise... ? &lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Traditionally, getting an app on a friend's phone meant: Buying a Mac -&amp;gt; Installing 40GB of Xcode -&amp;gt; Wrestling with Provisioning Profiles -&amp;gt; Understand Build Configs -&amp;gt; Self Doubt -&amp;gt;... Is it working now??&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;Using Expo Launch (EAS) to handle all of the above.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Goal
&lt;/h3&gt;

&lt;p&gt;Start from scratch using "create-expo-app" npm package, ask Cursor to make a sample app, and end with a Apple invite email on a friend's device for them to install and run.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pre-Flight Checklist (Minutes 0–5)
&lt;/h2&gt;

&lt;p&gt;To hit the sub-one-hour mark, I need these ready:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;GitHub&lt;/strong&gt; account&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An &lt;strong&gt;Apple Developer Program&lt;/strong&gt; membership ($99/year).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An &lt;strong&gt;Expo&lt;/strong&gt; account, preferably paid to get in the priority queue ($20 USD per month).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cursor&lt;/strong&gt; with minimum AI coding tools&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Node.js&lt;/strong&gt;, &lt;strong&gt;bun&lt;/strong&gt; and relevant libraries installed locally.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;NB. &lt;strong&gt;No need&lt;/strong&gt; for a Mac nor XCode&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Phase 1: The App Creation (Minutes 5–15)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Run the command:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx create-expo-app@latest recipes-collect &lt;span class="nt"&gt;--template&lt;/span&gt; blank
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Open in Cursor and create a page by asking it something like:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please make a nice index screen and put the title on "Recipes Collect". give it a friendly, warm theme, orange based colour scheme. 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to add a nice icon for additional touch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plz make a new app icon to replace the default one
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result:&lt;br&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%2Fqkon0tpn9yaww2y18mf5.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%2Fqkon0tpn9yaww2y18mf5.png" alt=" "&gt;&lt;/a&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Git Init &amp;amp; Push:&lt;/strong&gt; Push this to a public GitHub repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvifivve3yzh5ohp7ipn.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%2Fbvifivve3yzh5ohp7ipn.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Phase 2: The Expo Launch Magic (Minutes 15–30)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;code&gt;launch.expo.dev&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Copy and Paste&lt;/strong&gt; the new GitHub link.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2ycb9rsrpr75oleoht6.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%2Fg2ycb9rsrpr75oleoht6.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Phase 3: The "Coffee Break" Build (Minutes 30–50)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Hit "Launch to App Store."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wait for the spinners and progress bars.    &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Phase 4: The Moment of Truth (Minutes 50–60)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;App created on App Store (pending for review mode)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the friend's email address as the "Internal Tester" of the app. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The email arrives. "Accept" first before the second TestFlight email will be sent. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fse4ck8cj5fshh5m4p4lq.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%2Fse4ck8cj5fshh5m4p4lq.PNG" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Install it and have it shown on the phone&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%2Fha5se9t9u8gfxdw3ctfq.jpeg" 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%2Fha5se9t9u8gfxdw3ctfq.jpeg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Verdict:&lt;/strong&gt; Did we make it under 60 minutes? Yes, verified!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It certainly is magical, to have the whole process so quick and easy! Almost no code, except for typing a few GitHub commands. &lt;/p&gt;

&lt;p&gt;Of course, the use case is limited - one can not have very complicated app (eg requires native build tools, backend services, APIs, etc). And the GitHub repo would need to be configured to allow for EAS access or stay public. But, for rapid prototyping, look and feel, market validation, it seems a good choice. &lt;/p&gt;

&lt;p&gt;I will definitely research into it a bit more - and share my findings here! &lt;/p&gt;
&lt;h2&gt;
  
  
  Video of the whole process
&lt;/h2&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/KvwFDNnkw-k"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;EAS Launch : &lt;a href="//launch.expo.dev"&gt;launch.expo.dev&lt;/a&gt;&lt;br&gt;
Apple App Store: How to &lt;a href="https://dev.to/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-testers-and-apple-testflight-part-35-eoo"&gt;add Internal Testers&lt;/a&gt;&lt;/p&gt;

</description>
      <category>expo</category>
      <category>buildinpublic</category>
      <category>mobile</category>
      <category>challenge</category>
    </item>
    <item>
      <title>How to Build and Test iOS Apps on a Physical Phone: Sending Updates (Part 5/5)</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Fri, 06 Mar 2026 02:25:15 +0000</pubDate>
      <link>https://forem.com/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-sending-updates-part-55-1gj2</link>
      <guid>https://forem.com/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-sending-updates-part-55-1gj2</guid>
      <description>&lt;p&gt;Now that we have our app on TestFlight, and gotten the testers feedback (in &lt;a href="https://dev.to/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-adding-external-testers-part-45-5e3g"&gt;Part 4&lt;/a&gt;), we can now send the fixes!  &lt;/p&gt;

&lt;p&gt;The best way to do this is to use Over The Air (OTA) updates, which bypasses Apple’s Reviewing system so the users get instant changes. &lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;p&gt;Using EAS update is good for JS updates, but not for native code changes. So, if something does &lt;strong&gt;not&lt;/strong&gt; require a new Build (eg eas build), then we can send the new code using this method.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: Apple’s guidelines allow OTA updates for bug fixes/minor features, but using it to significantly change the app's purpose can get an account banned.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  How the Phone Receives the Updates
&lt;/h3&gt;

&lt;p&gt;Unlike a website, where the updates can be patched onto the server for the users to receive them; the app is a binary already compiled and only lives on users’ phones. Therefore, a few things we need to check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is the version of the user’s binary? (i.e. “runtime version”)?&lt;/li&gt;
&lt;li&gt;What “channel” is this binary? We can only send the updates if it’s the correct channel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that the users will need to relaunch the app to receive the updates.&lt;/p&gt;

&lt;p&gt;So here are the config files setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;eas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;

&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;autoIncrement&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;distribution&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;channel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;// &amp;lt;--- this is the channel&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;

  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;expo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;runtimeVersion&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;      &lt;span class="c1"&gt;// &amp;lt;--- this is the runtime version&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;policy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;appVersion&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;// &amp;lt;--- use app version (eg 1.0.1) &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;blockquote&gt;
&lt;p&gt;Tip: if using appVersion as the policy, we must increment the version number in app.json for every new store build, or the updates might not link correctly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  EAS “branch” feature
&lt;/h3&gt;

&lt;p&gt;EAS allows for an easy way to switch between different versions of the updates using “branches”. This is handy for &lt;/p&gt;

&lt;p&gt;⚡ Instant rollback&lt;/p&gt;

&lt;p&gt;⚡ Gradual rollouts&lt;/p&gt;

&lt;p&gt;⚡ A/B testing&lt;/p&gt;

&lt;p&gt;⚡ Switching update streams&lt;/p&gt;

&lt;p&gt;All &lt;strong&gt;without&lt;/strong&gt; rebuilding the app.&lt;/p&gt;

&lt;p&gt;We can think of our user’s phone as a receiver like a TV and its frequency:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TV Concept&lt;/th&gt;
&lt;th&gt;Expo / EAS Concept&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TV&lt;/td&gt;
&lt;td&gt;Installed mobile app build&lt;/td&gt;
&lt;td&gt;The binary installed on the user's phone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Channel frequency&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Branch / Channel&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Which stream of updates the app listens to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broadcast format&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Runtime version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Is the update compatible with the binary?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TV broadcast&lt;/td&gt;
&lt;td&gt;OTA update&lt;/td&gt;
&lt;td&gt;The code pushed to users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No signal&lt;/td&gt;
&lt;td&gt;Update not received&lt;/td&gt;
&lt;td&gt;Build ignores &lt;strong&gt;incompatible&lt;/strong&gt; update&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Practical Steps
&lt;/h3&gt;

&lt;p&gt;Now that we understand the concepts, the process should be easy.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1
&lt;/h4&gt;

&lt;p&gt;Check the app.json and eas.json are set up properly (see above) on the channel and run time version policy.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2
&lt;/h4&gt;

&lt;p&gt;Make changes to our RN codebase - make sure no native modules - and push it to GitHub&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;git&lt;/span&gt; &lt;span class="nx"&gt;push&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 3
&lt;/h4&gt;

&lt;p&gt;Install and configure eas-update&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;expo&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;expo&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;updates&lt;/span&gt;

&lt;span class="nx"&gt;eas&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;configure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 4
&lt;/h4&gt;

&lt;p&gt;Make sure the “channel“ of our build maps to the correct EAS update “branch”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Channel&lt;/strong&gt; = &lt;strong&gt;compiled into our build,&lt;/strong&gt; and listens to whatever is connected to it&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Branch&lt;/strong&gt; = can be mapped into a channel. This can be changed even when app has already launched - to test new features, etc without needing the users to re-download the app, eg for A/B Testing.&lt;/p&gt;

&lt;p&gt;To match production channel to a production branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;eas&lt;/span&gt; &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;edit&lt;/span&gt; &lt;span class="nx"&gt;production&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt; &lt;span class="nx"&gt;production&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 5
&lt;/h4&gt;

&lt;p&gt;Send the updates (optional msg)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;eas&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt; &lt;span class="nx"&gt;production&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;small paragraph&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 6
&lt;/h4&gt;

&lt;p&gt;Check the app again by relaunching it.&lt;/p&gt;

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

&lt;p&gt;Watch this 3 mins video as I demo all the steps and see the instant changes on the phone!&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/Sl7Y6f2oCnU"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>expo</category>
      <category>appstore</category>
      <category>mobile</category>
      <category>ios</category>
    </item>
    <item>
      <title>How to Build and Test iOS Apps on a Physical Phone: Adding External Testers (Part 4/5)</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Mon, 02 Mar 2026 22:08:11 +0000</pubDate>
      <link>https://forem.com/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-adding-external-testers-part-45-5e3g</link>
      <guid>https://forem.com/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-adding-external-testers-part-45-5e3g</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-testers-and-apple-testflight-part-35-eoo"&gt;Part 3&lt;/a&gt;, we invited close friends and colleagues to test on their phones (internal testers). Now it's time to invite the wider audience (clients, investors, etc.)!&lt;/p&gt;

&lt;h2&gt;
  
  
  External Testers
&lt;/h2&gt;

&lt;p&gt;This is reasonably straightforward. Just go to the TestFlight section, find the Add Group button.&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%2F1xjz1938aa5pzt6ewv74.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1xjz1938aa5pzt6ewv74.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The scroll to the bottom of the page, find the list of Builds. Add the Group to a Build.&lt;/p&gt;

&lt;p&gt;Notice that the Status will become "Waiting for Review".&lt;br&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%2Fdazyqnlfq4j97w6aafsl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdazyqnlfq4j97w6aafsl.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For each new Version, Apple will need a Beta App Review which will usually take &lt;strong&gt;1-2 days&lt;/strong&gt;. The Builds under a Version, however, won't need the review, provided no significant changes were made to the app's metadata or permissions. &lt;/p&gt;

&lt;p&gt;If we make a new Build, we can enabled "automatic distribution" for emails to be sent out. Otherwise we will need to invite the (External Testers) Group every single time. We can set this up at the time we add a Build to our new Group. Refer to App Store Connect &lt;a href="https://developer.apple.com/help/app-store-connect/test-a-beta-version/invite-external-testers" rel="noopener noreferrer"&gt;Invite External Testers section&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Process Flow of External Testers
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ 1. EAS SUBMIT ] -------&amp;gt; (Run 'eas submit -p ios')
   |                     (Sends build to App Store Connect)
   v
[ 2. APPLE PROCESSING ] -&amp;gt; (Wait 5-20 mins for Apple to "process")
   |
   v
[ 3. TESTFLIGHT TAB ] ----&amp;gt; (Create "External Group")
   |
   v
[ 4. ADD TEST GROUP ] ---------&amp;gt; (External Testers to EAS build)
   | 
   v
[ 5. (EACH VER) BETA REVIEW ] -------&amp;gt; (Apple takes 24-48 hours to approve for each version)
   |
   v
[ 6. TESTFLIGHT App ] ------&amp;gt; (Testers get notified via Email)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;We will learn to send "updates" (code changes) in our &lt;a href="https://dev.to/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-sending-updates-part-55-1gj2"&gt;final part&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;
  
  
  Prefer to Watch a Video?
&lt;/h2&gt;

&lt;p&gt;Follow me as I walk thru this config process, plus a demo when a user receives an email to use TestFlight - how to download, install, and use our beta app. &lt;br&gt;


  &lt;iframe src="https://www.youtube.com/embed/XpiziMGis4c"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>ios</category>
      <category>mobile</category>
      <category>appstore</category>
    </item>
    <item>
      <title>How to Build and Test iOS Apps on a Physical Phone: Adding Internal Testers (Part 3/5)</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Wed, 25 Feb 2026 23:44:38 +0000</pubDate>
      <link>https://forem.com/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-testers-and-apple-testflight-part-35-eoo</link>
      <guid>https://forem.com/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-testers-and-apple-testflight-part-35-eoo</guid>
      <description>&lt;p&gt;Now we have submitted our app to the App Store in &lt;a href="https://dev.to/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-expo-eas-and-apple-testflight-part-23-4ff8"&gt;Step 2&lt;/a&gt;, now it's time to invite friends and family to use it!&lt;/p&gt;

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

&lt;p&gt;Everyone with an Apple ID can be invited now to test on their phones. The phones' UUIDs don't need to be registered with Apple.&lt;/p&gt;

&lt;p&gt;There are two different types of Testers: Internal and External.&lt;/p&gt;

&lt;h3&gt;
  
  
  Internal Testing
&lt;/h3&gt;

&lt;p&gt;These are usually colleagues in the same company. For sole/indie developers, they can be anyone we wish to invite every time you make a new build during the testing phase. They don't have to have the same email domain as ours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Add as a New User&lt;br&gt;
Add the friend in &lt;a href="https://appstoreconnect.apple.com/access/users" rel="noopener noreferrer"&gt;https://appstoreconnect.apple.com/access/users&lt;/a&gt;&lt;br&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%2F4h9rfmf0jz6qlhz2a5c4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4h9rfmf0jz6qlhz2a5c4.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill up the details and then Invite. &lt;br&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%2Ftgq666l61r9c223h5wcr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftgq666l61r9c223h5wcr.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The friend will receive an email from the App Store.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: Accept via the Email&lt;br&gt;
When they receive an email, Accept the invitation.&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%2F8f3rlaolwfekg0tgf1t7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8f3rlaolwfekg0tgf1t7.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;: Add to the Internal Testers Group&lt;br&gt;
If it's the first build, developers must go to the build's Test Information to provide a "What To Test" description.&lt;/p&gt;

&lt;p&gt;Go back to our App section, under TestFlight tab and scroll to the top:&lt;/p&gt;

&lt;p&gt;Create a New Internal Group:&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%2Fkkkcsffaxd0ucxoh58bp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkkkcsffaxd0ucxoh58bp.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then add Testers:&lt;br&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%2Ff178kdu86sck26hbty8j.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff178kdu86sck26hbty8j.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

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

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

&lt;p&gt;You can check this in the iOS TestFlight Build tab:&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%2Fidumtnb4wvki7rybx3kb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fidumtnb4wvki7rybx3kb.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Next, we will add External Testers in &lt;a href="https://dev.to/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-adding-external-testers-part-45-5e3g"&gt;Part 4&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;
  
  
  Video
&lt;/h2&gt;

&lt;p&gt;Here's a video of the walk through.&lt;br&gt;


  &lt;iframe src="https://www.youtube.com/embed/6BanAJwDaqM"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>ios</category>
      <category>mobile</category>
      <category>appstore</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>How I Published My First Tutorial Video in 2 Days instead of 5 Years</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Tue, 17 Feb 2026 18:30:39 +0000</pubDate>
      <link>https://forem.com/cathylai/how-i-published-my-first-tutorial-video-in-2-days-instead-of-5-years-87</link>
      <guid>https://forem.com/cathylai/how-i-published-my-first-tutorial-video-in-2-days-instead-of-5-years-87</guid>
      <description>&lt;p&gt;It has always been in my heart to create video tutorials like those ones I follow on YouTube. But for years, I would: open YouTube Studio, check microphone reviews, compare cameras, and analyze the works of top-tier creators. Then I’d close the tabs, create a large todo list, and produce zero videos.&lt;/p&gt;

&lt;p&gt;Everything changed when I shifted to a &lt;strong&gt;Minimum Viable Output&lt;/strong&gt; mindset. I stopped optimizing for ideal production and started optimizing for clarity and speed. Instead of a fancy camera and complex live command execution, I &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Used mic and screencast functions on MacBook&lt;/li&gt;
&lt;li&gt;Used Notion as my "PowerPoint" &lt;/li&gt;
&lt;li&gt;Edited out word gaps with an online tool &lt;a href="https://app.gling.ai/app/" rel="noopener noreferrer"&gt;Gling&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Swapped risky live CLI demos for clear screenshots of completed steps &lt;/li&gt;
&lt;li&gt;Used no music, no transitions — just teaching.&lt;/li&gt;
&lt;li&gt;Made a quick thumbnail with Nano Banana&lt;/li&gt;
&lt;li&gt;Only spent the longest time making sure the info are well researched and is verified a couple of times.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Perfectionist vs. Creator Mindset
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;The Perfectionist Mindset&lt;/th&gt;
&lt;th&gt;The Creator Mindset&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Earning peer respect &amp;amp; looking "senior."&lt;/td&gt;
&lt;td&gt;Delivering immediate value to the learner.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Technical Setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Professional mics, 4K cameras, and lighting.&lt;/td&gt;
&lt;td&gt;Notion/Docs, screen recording, and clear audio.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Handling Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Risky live coding and CLI builds.&lt;/td&gt;
&lt;td&gt;Screenshots and pre-recorded, verified snippets.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;View of Imperfections&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A failure of professional standards.&lt;/td&gt;
&lt;td&gt;A natural part of the iterative learning process.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Post-Production&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cinematic intros and complex transitions.&lt;/td&gt;
&lt;td&gt;Focus on pacing and information density.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output Velocity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5 years of "researching" and 0 videos.&lt;/td&gt;
&lt;td&gt;2 days of "shipping" and 1 live tutorial.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;The Outcome&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High standards with &lt;strong&gt;zero impact&lt;/strong&gt;.&lt;/td&gt;
&lt;td&gt;Functional clarity with &lt;strong&gt;maximum momentum&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Result
&lt;/h3&gt;

&lt;p&gt;Once I “forced” myself to publish, I felt the new door has now opened for me. I now know where to improve (eg thumbnail, SEO, length, speed, and clarity), instead of guessing. I have clear next steps, and I know I will contiguously make improvements. Nobody laughed, instead everybody applauded me for just getting started:)&lt;/p&gt;

&lt;h3&gt;
  
  
  The Video
&lt;/h3&gt;

&lt;p&gt;If you’re interested, here’s the result of the video!&lt;br&gt;


  &lt;iframe src="https://www.youtube.com/embed/zTJDTj5AjFs"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>devjournal</category>
      <category>todayilearned</category>
      <category>youtube</category>
    </item>
    <item>
      <title>How to Build and Test iOS Apps on a Physical Phone: Expo EAS and Apple TestFlight (Part 2/5)</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Mon, 16 Feb 2026 00:52:45 +0000</pubDate>
      <link>https://forem.com/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-expo-eas-and-apple-testflight-part-23-4ff8</link>
      <guid>https://forem.com/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-expo-eas-and-apple-testflight-part-23-4ff8</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-expo-eas-and-apple-testflight-part-13-357o"&gt;Part 1&lt;/a&gt;, we've checked in the React Native code on the GitHub. Now we will build the binary with Expo EAS (Expo Application Service) service.&lt;/p&gt;

&lt;h2&gt;
  
  
  EAS Build
&lt;/h2&gt;

&lt;p&gt;Production (store-signed) binary&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eas build &lt;span class="nt"&gt;--platform&lt;/span&gt; ios &lt;span class="nt"&gt;--profile&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted, let EAS manage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;certificates&lt;/li&gt;
&lt;li&gt;provisioning profiles&lt;/li&gt;
&lt;li&gt;signing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This produces an &lt;strong&gt;App Store–signed IPA&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Resolved &lt;span class="s2"&gt;"production"&lt;/span&gt; environment &lt;span class="k"&gt;for &lt;/span&gt;the build. Learn more: https://docs.expo.dev/eas/environment-variables/#setting-the-environment-for-your-builds
....

✔ Incremented buildNumber from x to y.

✔ Using remote iOS credentials &lt;span class="o"&gt;(&lt;/span&gt;Expo server&lt;span class="o"&gt;)&lt;/span&gt;

If you provide your Apple account credentials we will be able to generate all necessary build credentials and fully validate them.
...
✔ Do you want to log &lt;span class="k"&gt;in &lt;/span&gt;to your Apple account? … &lt;span class="nb"&gt;yes&lt;/span&gt;

› Log &lt;span class="k"&gt;in &lt;/span&gt;to your Apple Developer account to &lt;span class="k"&gt;continue&lt;/span&gt;
✔ Apple ID: … cathy.xxxx@xxxxx.com
› Restoring session /Users/cathy/.app-store/auth/cathy.xxxx@xxxxx.com/cookie
› Session expired Local session
› Using password &lt;span class="k"&gt;for &lt;/span&gt;cathy.xxxx@xxxxx.com from your &lt;span class="nb"&gt;local &lt;/span&gt;Keychain
  Learn more: https://docs.expo.dev/distribution/security#keychain
✔ Logged &lt;span class="k"&gt;in &lt;/span&gt;New session
› Team Cathy Lai &lt;span class="o"&gt;(&lt;/span&gt;XXXXXX&lt;span class="o"&gt;)&lt;/span&gt;
› Provider Cathy Lai &lt;span class="o"&gt;(&lt;/span&gt;xxxxxxxx&lt;span class="o"&gt;)&lt;/span&gt;
✔ Bundle identifier registered com.cathyapp1234.oauthpro2
✔ Synced capabilities: No updates
✔ Synced capability identifiers: No updates
✔ Fetched Apple distribution certificates
✔ Fetched Apple provisioning profiles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again the bundle identifier is unique in the App Store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Project Credentials Configuration

Project                   @cathyapp1234/oauth-pro2
Bundle Identifier         com.cathyapp1234.oauthpro2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Distribution Certificate and Provisioning Profiles are auto generated. It is the "permission slip" from Apple to allow the binary to run on the specific phones.  &lt;/p&gt;

&lt;p&gt;In the Apple ecosystem, we can’t just drag and drop an app file onto an iPhone like we can with a .exe on a PC. Apple requires a strict chain of trust to ensure that the app is legitimate, created by a verified developer, and running on an authorized device.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;App Store Configuration   

Distribution Certificate  
Serial Number             XXXXXXXDA97EA34FFC3B28C8BA6C44
Expiration Date           Tue, 04 Aug 2026 05:10:17 GMT+1200
Apple Team                XXXXXX &lt;span class="o"&gt;(&lt;/span&gt;Cathy Lai &lt;span class="o"&gt;(&lt;/span&gt;Individual&lt;span class="o"&gt;))&lt;/span&gt;
Updated                   6 months ago

Provisioning Profile      
Developer Portal ID       XxXXXXXXXX
Status                    active
Expiration                Tue, 04 Aug 2026 05:10:17 GMT+1200
Apple Team                XXXXXXXXXX &lt;span class="o"&gt;(&lt;/span&gt;Cathy Lai &lt;span class="o"&gt;(&lt;/span&gt;Individual&lt;span class="o"&gt;))&lt;/span&gt;
Updated                   17 days ago

All credentials are ready to build @cathyapp1234/oauth-pro2 &lt;span class="o"&gt;(&lt;/span&gt;com.cathyapp1234.oauthpro2&lt;span class="o"&gt;)&lt;/span&gt;

Compressing project files and uploading to EAS Build. Learn more: https://expo.fyi/eas-build-archive
✔ Uploaded to EAS 1s
✔ Computed project fingerprint

See logs: https://expo.dev/accounts/cathyapp1234/projects/oauth-pro2/builds/xxxxxxx

Waiting &lt;span class="k"&gt;for &lt;/span&gt;build to complete. You can press Ctrl+C to exit.
  Build queued...

Waiting &lt;span class="k"&gt;in &lt;/span&gt;priority queue
|■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■| 

✔ Build finished
🍏 iOS app:
https://expo.dev/artifacts/eas/xxxxxxxxx.ipa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  EAS submit
&lt;/h2&gt;

&lt;p&gt;Then we submit to the same profile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eas submit --profile production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the prompts to select the correct configs (platforms, etc) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✔ Select platform › iOS

✔ What would you like to submit? › Select a build from EAS
✔ Which build would you like to submit? › - ID: 5841b2f1-b3c1-4b4b-93d2-b3b587744843 (11 days ago)
Ensuring your app exists on App Store Connect. This step can be skipped by providing ascAppId in the
submit profile. Learn more: https://expo.fyi/asc-app-id

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

&lt;/div&gt;



&lt;p&gt;Login again for App Store&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;› Log in to your Apple Developer account to continue
✔ Apple ID: … cathyl.apple@gmail.com
› Restoring session /Users/cathy/.app-store/auth/cathyl.apple@gmail.com/cookie
› Session expired Local session
› Using password for cathyl.apple@gmail.com from your local Keychain
  Learn more: https://docs.expo.dev/distribution/security#keychain
✔ Logged in, verify your Apple account to continue
Two-factor Authentication (6 digit code) is enabled for cathyl.apple@gmail.com. Learn more: https://support.apple.com/en-us/HT204915

✔ How do you want to validate your account? … device / sms
✔ Please enter the 6 digit code … 429798
✔ Valid code
✔ Logged in and verified
› Team Cathy Lai (VJ2LKA46QZ)
› Provider Cathy Lai (124547644)

✔ Bundle identifier registered com.cathyapp1234.oauthpro2
✔ Prepared App Store Connect for oauth-pro2 com.cathyapp1234.oauthpro2
Looking up credentials configuration for com.cathyapp1234.oauthpro2...
✔ App Store Connect API Key already set up.
Using Api Key ID: VWQTD6L5L3 ([Expo] EAS Submit MnT3S3wHKr)

ASC App ID:                 6758167617
Project ID:                 a31c0453-d16e-4818-aa19-d380a5bfc5d4
App Store Connect API Key:  
    Key Name  :  [Expo] EAS Submit MnT3S3wHKr
    Key ID    :  VWQTD6L5L3
    Key Source:  EAS servers
Build:                      
    Build ID    :  5841b2f1-b3c1-4b4b-93d2-b3b587744843
    Build Date  :  13/02/2026, 8:36:43 am
    App Version :  1.0.0
    Build number:  7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then EAS will schedule the submission:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✔ Scheduled iOS submission

Submission details: xxxx
Waiting for submission to complete. You can press Ctrl+C to exit.
⠼
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expo will auto-create the app in App Store Connect&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  App record created&lt;/li&gt;
&lt;li&gt;  Bundle ID registered&lt;/li&gt;
&lt;li&gt;  Build uploaded&lt;/li&gt;
&lt;li&gt;  Appears in &lt;strong&gt;TestFlight&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;We will add some testers (via emails) in &lt;a href="https://dev.to/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-testers-and-apple-testflight-part-35-eoo"&gt;Part 3&lt;/a&gt;so they can be notified and get a link to TestFlight to access our app. &lt;/p&gt;

&lt;h2&gt;
  
  
  Join Me
&lt;/h2&gt;

&lt;p&gt;Watch me build my prototype app and submit it to the App Store! &lt;br&gt;


  &lt;iframe src="https://www.youtube.com/embed/zTJDTj5AjFs"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>expo</category>
      <category>mobile</category>
      <category>ios</category>
    </item>
    <item>
      <title>How to Build and Test iOS Apps on a Physical Phone: Expo EAS and Apple TestFlight (Part 1/5)</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Tue, 10 Feb 2026 00:53:54 +0000</pubDate>
      <link>https://forem.com/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-expo-eas-and-apple-testflight-part-13-357o</link>
      <guid>https://forem.com/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-expo-eas-and-apple-testflight-part-13-357o</guid>
      <description>&lt;p&gt;So you have turned a bright idea into a release-ready iOS app, time to test it properly by friends and family on their physical phones! &lt;/p&gt;

&lt;p&gt;This 5-part article will explain how we can go from an app created on our PC/laptop to the Apple App Store, via an app called TestFlight which enables external testers to use the preview version of our app.&lt;/p&gt;

&lt;p&gt;The analogy (from &lt;a href="https://dev.to/cathylai/run-an-ios-app-on-the-physical-phone-demystified-146k"&gt;this article&lt;/a&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We (the developers): The brand owners&lt;/li&gt;
&lt;li&gt;Our app: The product&lt;/li&gt;
&lt;li&gt;EAS (Expo Application Service): The manufacturer + logistics company&lt;/li&gt;
&lt;li&gt;Apple App Store: The supermarket&lt;/li&gt;
&lt;li&gt;TestFlight: supermarket's tasting room. The product (our app) must be "registered" first before it can be entered in this room&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 1 — Identity &amp;amp; Setup
&lt;/h2&gt;

&lt;p&gt;This part connects &lt;strong&gt;your local project&lt;/strong&gt;, &lt;strong&gt;Expo&lt;/strong&gt;, and &lt;strong&gt;Apple&lt;/strong&gt; so they all agree on your app’s identity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1/7 : Install and log in to EAS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; eas-cli
eas login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2/7 : Initialize EAS in your project
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eas init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This links your repo to an Expo project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3/7 : Verify &lt;code&gt;app.json&lt;/code&gt; (or &lt;code&gt;app.config.*&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Make sure these two things are correct:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ios.bundleIdentifier&lt;/code&gt;&lt;/strong&gt;
This must match what you’ll use in App Store Connect.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"expo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"oauth-pro2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ios"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"bundleIdentifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.cathyapp1234.oauthpro2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;extra.eas.projectId&lt;/code&gt;&lt;/strong&gt;
This links your local project to Expo EAS.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"expo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"extra"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"eas"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="nl"&gt;"projectId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a31c0453-d16e-4818-aa19-d380a5bfc5d4"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  Step 4/7: Define &lt;em&gt;build&lt;/em&gt; Profiles in &lt;code&gt;eas.json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Regardless of a build profile (eg "preview" or "production"), there are two types of builds: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ad Hoc Build, binary is on Expo :
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"distribution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Signed and is permitted to be on the App Store
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"distribution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"store"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are building the latter so we can download it via TestFlight.&lt;/p&gt;

&lt;p&gt;Also, it is useful to auto increment the build number with every EAS build&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"autoIncrement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The eas.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"preview"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"distribution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"store"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="nl"&gt;"channel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"preview"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;     
      &lt;/span&gt;&lt;span class="nl"&gt;"autoIncrement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;



&lt;p&gt;The convention is to use EAS remote build number instead of keeping tracks of it ourselves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cli"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"appVersionSource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"remote"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this system, build number is a single counter tied to your &lt;strong&gt;Project ID&lt;/strong&gt; and &lt;strong&gt;Platform&lt;/strong&gt; (iOS), not to a specific build profile. This means, both&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eas build &lt;span class="nt"&gt;--profile&lt;/span&gt; preview
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eas build &lt;span class="nt"&gt;--profile&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will both increase the same build number. &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5/7: Add the Corresponding &lt;em&gt;submit&lt;/em&gt; Profiles
&lt;/h3&gt;

&lt;p&gt;Make sure the entries are available, even if empty. Otherwise the build will give errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"preview"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Final eas.json can be found in &lt;a href="https://github.com/pscientist/oauth-pro2/blob/main/eas.json" rel="noopener noreferrer"&gt;this GitHub repository&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6/7: Channels and OTA (Over the Air Updates)
&lt;/h3&gt;

&lt;p&gt;Since we have specify different channels for each build profile, we can send code updates (bug fixes, small changes) over the air (OTA) via the channels to phones that already have the app installed, without having to resubmit to the App Store.&lt;br&gt;
You can choose to skip this step, but EAS will give a warning.&lt;/p&gt;

&lt;p&gt;Run the followings for this function&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx expo &lt;span class="nb"&gt;install &lt;/span&gt;expo-updates
eas update:configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7/7: Check into remote GitHub
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we are ready to build the binary in &lt;a href="https://dev.to/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-expo-eas-and-apple-testflight-part-23-4ff8"&gt;Part 2&lt;/a&gt;! &lt;/p&gt;

&lt;h2&gt;
  
  
  Prefer to Watch a Video?
&lt;/h2&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/zTJDTj5AjFs"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>expo</category>
      <category>ios</category>
      <category>mobile</category>
    </item>
    <item>
      <title>I Lost 2 Hours to an AI Search Answer — Here’s What I Learned About Context Engineering</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Mon, 02 Feb 2026 04:01:59 +0000</pubDate>
      <link>https://forem.com/cathylai/i-lost-2-hours-to-an-ai-search-answer-heres-what-i-learned-about-context-engineering-5787</link>
      <guid>https://forem.com/cathylai/i-lost-2-hours-to-an-ai-search-answer-heres-what-i-learned-about-context-engineering-5787</guid>
      <description>&lt;h2&gt;
  
  
  The Issue
&lt;/h2&gt;

&lt;p&gt;Last week, I tried to use Perplexity AI to teach me about EAS Builds. I got thrown off everywhere was confused for a long time! Here're the sequence of questions I asked:&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%2Fpqr3cuta183vvi5khnqj.jpeg" 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%2Fpqr3cuta183vvi5khnqj.jpeg" alt=" " width="411" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then &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%2F6vndekd519cbggufnlg4.jpeg" 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%2F6vndekd519cbggufnlg4.jpeg" alt=" " width="360" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's all well and good, within the context of EAS Build variations. &lt;/p&gt;

&lt;h3&gt;
  
  
  Problematic follow-up question
&lt;/h3&gt;

&lt;p&gt;I assumed the below is also within the context of the conversations above, but it gave me a surprising answer: &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%2Fx3eutgda8o89mizi3lhm.jpeg" 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%2Fx3eutgda8o89mizi3lhm.jpeg" alt=" " width="344" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the first reference returned was a 2019 article talked about &lt;strong&gt;unrelated&lt;/strong&gt; structure - preview, production and testing branching of the source code on GitHub, not EAS Build config. &lt;/p&gt;

&lt;h3&gt;
  
  
  What I thought I asked
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  I’m talking about &lt;strong&gt;&lt;code&gt;eas.json&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  EAS build profiles (&lt;code&gt;development&lt;/code&gt;, &lt;code&gt;preview&lt;/code&gt;, &lt;code&gt;production&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;distribution: "store"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  TestFlight vs App Store release&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a &lt;strong&gt;binary signing &amp;amp; distribution&lt;/strong&gt; question.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Perplexity answered (what it &lt;em&gt;assumed&lt;/em&gt;)
&lt;/h3&gt;

&lt;p&gt;Perplexity cited &lt;a href="https://dev.to/calintamas/how-to-manage-staging-and-production-environments-in-a-react-native-app-4naa"&gt;this article&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;How to manage staging and production environments in a React Native app&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At first glance, it &lt;em&gt;sounds&lt;/em&gt; relevant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  staging vs production&lt;/li&gt;
&lt;li&gt;  React Native&lt;/li&gt;
&lt;li&gt;  real-world apps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But that article is about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  environment variables&lt;/li&gt;
&lt;li&gt;  API base URLs
&lt;/li&gt;
&lt;li&gt;  runtime configuration&lt;/li&gt;
&lt;li&gt;  feature flags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;strong&gt;It has nothing to do with EAS Build, TestFlight, App Store Connect, or signing.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Perplexity didn’t “infer” my earlier context
&lt;/h3&gt;

&lt;p&gt;This was the key realization:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Perplexity does not reliably carry context across separate queries.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each question is treated as a fresh retrieval task:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; interpret keywords&lt;/li&gt;
&lt;li&gt; fetch high-ranking related pages&lt;/li&gt;
&lt;li&gt; synthesize an answer&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If most indexed content around “staging vs production” is about environment variables, that’s what it will pull — even if &lt;em&gt;you&lt;/em&gt; meant EAS build profiles.&lt;/p&gt;

&lt;h3&gt;
  
  
  The fix: anchoring the context (aka context engineering)
&lt;/h3&gt;

&lt;p&gt;What actually works is &lt;strong&gt;pinning the domain so the AI can’t drift&lt;/strong&gt;.&lt;br&gt;
Things that help &lt;em&gt;a lot&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  naming concrete artifacts: &lt;code&gt;eas.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  naming commands: &lt;code&gt;eas build&lt;/code&gt;, &lt;code&gt;eas submit&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  naming platforms: TestFlight, App Store Connect&lt;/li&gt;
&lt;li&gt;  explicitly excluding irrelevant meanings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example of a &lt;em&gt;much better&lt;/em&gt; prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I am using Expo EAS. In &lt;code&gt;eas.json&lt;/code&gt;, I have build profiles like &lt;code&gt;development&lt;/code&gt;, &lt;code&gt;preview&lt;/code&gt;, and &lt;code&gt;production&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
Explain the difference between a preview build and a production build &lt;strong&gt;in terms of signing and distribution&lt;/strong&gt; (TestFlight vs App Store).&lt;br&gt;&lt;br&gt;
Ignore articles about staging/production environment variables.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Even better: paste a snippet of the &lt;code&gt;eas.json&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There is still some technical details to learn about AI tools, even though it uses plain English and feels "easy" to use! I definitely need to learn more for it to be useful in solving my everyday issues.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>expo</category>
      <category>buildinpublic</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>Ways to Run Your Expo iOS App on a Physical Phone (Dev, Preview &amp; TestFlight Explained)</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Tue, 27 Jan 2026 16:11:02 +0000</pubDate>
      <link>https://forem.com/cathylai/run-an-ios-app-on-the-physical-phone-demystified-146k</link>
      <guid>https://forem.com/cathylai/run-an-ios-app-on-the-physical-phone-demystified-146k</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;So you have your app running on the iOS Simulator. What to do next to test, preview, and go live on the Apple App Store? &lt;/p&gt;

&lt;p&gt;Even though React Native makes mobile development feels like writing JavaScript, the deployment process is nothing like web deployment (one click publishing)! Below is a mental model I found helpful when I published my &lt;a href="https://apps.apple.com/us/app/planetfam/id6751137370" rel="noopener noreferrer"&gt;PlanetFam Quiz&lt;/a&gt; app last year.&lt;/p&gt;

&lt;h2&gt;
  
  
  App Store, EAS, TestFlight, different Builds - an supermarket analogy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The parties involved
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We (the developers): The brand owners&lt;/li&gt;
&lt;li&gt;Our app: The product&lt;/li&gt;
&lt;li&gt;EAS (Expo Application Service): The manufacturer + logistics company&lt;/li&gt;
&lt;li&gt;Apple App Store: The supermarket 

&lt;ul&gt;
&lt;li&gt;TestFlight: supermarket's tasting room. The product (our app) must be "registered" first before it can be entered in this room&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  The different app builds
&lt;/h3&gt;

&lt;p&gt;To run a React Native app on a physical iPhone without a Metro server, you must package the JavaScript code into an binary (best using EAS)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Build type&lt;/th&gt;
&lt;th&gt;Mental model&lt;/th&gt;
&lt;th&gt;Runs on&lt;/th&gt;
&lt;th&gt;Who is it for?&lt;/th&gt;
&lt;th&gt;Typical command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Expo Go&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🧪 Prototyping in the lab&lt;/td&gt;
&lt;td&gt;Simulator + physical phone&lt;/td&gt;
&lt;td&gt;Fast experimentation &amp;amp; learning&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npx expo start&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Development build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🍳 Internal test kitchen&lt;/td&gt;
&lt;td&gt;Simulator + physical phone&lt;/td&gt;
&lt;td&gt;Real-device testing during development&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eas build --profile development&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Preview build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🧾 Samples / staff or VIP - only stock&lt;/td&gt;
&lt;td&gt;Physical phone (via TestFlight)&lt;/td&gt;
&lt;td&gt;QA, stakeholders, real-world testing&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eas build --profile preview&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Production build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🏪 Retail-ready packaged goods&lt;/td&gt;
&lt;td&gt;App Store / TestFlight&lt;/td&gt;
&lt;td&gt;Public users&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eas build --profile production&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Technical requirements
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Build type&lt;/th&gt;
&lt;th&gt;Expo Go required?&lt;/th&gt;
&lt;th&gt;Phone registered with Apple (UUID)?&lt;/th&gt;
&lt;th&gt;Local Metro server needed?&lt;/th&gt;
&lt;th&gt;Source code in GitHub?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Expo Go&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Development build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Preview build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;No if via TestFlight, Yes if via Ad Hoc Build*&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Production build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Ad Hoc = installing a file directly; TestFlight = installing via Apple’s testing app."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common confusions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why a Production Build (+ submit) is needed
&lt;/h3&gt;

&lt;p&gt;The production build/submit step is &lt;strong&gt;not&lt;/strong&gt; shipping your app to users yet. We are doing one specific thing:&lt;/p&gt;

&lt;p&gt;Registering your product with the supermarket&lt;/p&gt;

&lt;p&gt;That registration creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  An App Store record&lt;/li&gt;
&lt;li&gt;  Distribution certificates&lt;/li&gt;
&lt;li&gt;  Provisioning profiles&lt;/li&gt;
&lt;li&gt;  Permission for Apple to host &lt;em&gt;any&lt;/em&gt; builds of this app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without that step:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  TestFlight doesn’t know your app exists&lt;/li&gt;
&lt;li&gt;  Preview builds have nowhere to land&lt;/li&gt;
&lt;li&gt;  Apple has no legal or technical context for your binary&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why are there two QR codes when I build a development/preview version ?
&lt;/h3&gt;

&lt;p&gt;When we run &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eas build --profile development
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We get this QR code which is the address of our build&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%2Fbandegct52tjxq19qs5h.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%2Fbandegct52tjxq19qs5h.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scan the above QR code to install the app (the app icon will show up).&lt;/p&gt;

&lt;p&gt;Then when we run the Metro server&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx expo start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The second (smaller) QR code appears. This is the Metro server address.&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%2Fj0577h3iihlgd7koy5to.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%2Fj0577h3iihlgd7koy5to.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scan this smaller QR code to hot reload the code changes. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;QR 1 (The Shell): This is the "house" for your app.&lt;/li&gt;
&lt;li&gt;QR 2 (The Furniture): This is the actual code/logic moving into the house.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ready to Start?
&lt;/h2&gt;

&lt;p&gt;Follow me as I deploy an React Native app from beginning to end, step by step, in 7 mins!&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/zTJDTj5AjFs"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions
&lt;/h2&gt;

&lt;p&gt;Please comment below and I will try to explain as much as possible.. &lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/cathylai/how-to-build-and-test-ios-apps-on-a-physical-phone-expo-eas-and-apple-testflight-part-13-357o"&gt;How to Build and Test iOS Apps on a Physical Phone: Expo EAS and Apple TestFlight&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>reactnative</category>
      <category>ios</category>
      <category>expo</category>
    </item>
  </channel>
</rss>
