<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: irinaivanovaalex</title>
    <description>The latest articles on DEV Community by irinaivanovaalex (@irinaivanovaalex).</description>
    <link>https://dev.to/irinaivanovaalex</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%2F1146089%2Faffa7ba3-018a-4ae6-9bdd-705d24e01e19.jpeg</url>
      <title>DEV Community: irinaivanovaalex</title>
      <link>https://dev.to/irinaivanovaalex</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/irinaivanovaalex"/>
    <language>en</language>
    <item>
      <title>How to launch a React Native app from the Lock Screen on iPhone</title>
      <dc:creator>irinaivanovaalex</dc:creator>
      <pubDate>Fri, 13 Dec 2024 19:16:36 +0000</pubDate>
      <link>https://dev.to/irinaivanovaalex/how-to-launch-a-react-native-app-from-the-lock-screen-on-iphone-m8g</link>
      <guid>https://dev.to/irinaivanovaalex/how-to-launch-a-react-native-app-from-the-lock-screen-on-iphone-m8g</guid>
      <description>&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%2Fvdogrypc7344ckp9196s.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%2Fvdogrypc7344ckp9196s.png" alt="Image description" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&gt;
With the release of iOS 18, it became possible to customize the Lock Screen. Naturally, this opens up opportunities to experiment and add interesting features to your React Native app. In this article, I'll explain how to make your app accessible directly from the Lock Screen.&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%2Fp0t1dgy50fumian8yysm.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0t1dgy50fumian8yysm.gif" alt="Image description" width="640" height="1386"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;To accomplish this, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;React Native&lt;/strong&gt; (I used version 0.76.3).&lt;/li&gt;
&lt;li&gt;Configured deep links. If you haven’t set this up yet, check out the guide: &lt;a href="https://reactnative.dev/docs/linking" rel="noopener noreferrer"&gt;https://reactnative.dev/docs/linking&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Steps to Get It Working
&lt;/h2&gt;
&lt;h4&gt;
  
  
  1. Create a Widget Template
&lt;/h4&gt;

&lt;p&gt;First, create a widget template using Xcode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;Targets -&amp;gt; "+"&lt;/strong&gt;, and select a widget.&lt;/li&gt;
&lt;li&gt;For this example, name it &lt;code&gt;QScan&lt;/code&gt;. Xcode will create two widgets: a regular widget and a Control Widget.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Control Widget&lt;/strong&gt; is the one you can place on the Lock Screen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the &lt;strong&gt;Control Widget&lt;/strong&gt;, you can choose between two types of interactions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Button&lt;/strong&gt; (&lt;em&gt;ControlWidgetButton&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Toggle&lt;/strong&gt; (&lt;em&gt;ControlWidgetToggle&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For my use case, I chose the first option: &lt;em&gt;ControlWidgetButton&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here’s the implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;QScanWidgetControl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ControlWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"QScanWidget"&lt;/span&gt;

 &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;ControlWidgetConfiguration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;StaticControlConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kt"&gt;ControlWidgetButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PerformAction&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="c1"&gt;// Button with a name and a system icon&lt;/span&gt;
   &lt;span class="kt"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"displayName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;systemImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"barcode.viewfinder"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"displayName"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"widgetDescription"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;PerformAction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AppIntent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;LocalizedStringResource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"displayName"&lt;/span&gt;

 &lt;span class="c1"&gt;// Determines if the app should automatically open when the intent is triggered.&lt;/span&gt;
 &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;openAppWhenRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

 &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

 &lt;span class="kd"&gt;@MainActor&lt;/span&gt;
 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;IntentResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"your deeplink"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kt"&gt;PerformError&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invalidURL&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="kt"&gt;EnvironmentValues&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Add App Groups
&lt;/h4&gt;

&lt;p&gt;For the Control Widget to open your app via deep links, you need to enable communication between the widget and the app. This is done using &lt;strong&gt;App Groups&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable &lt;strong&gt;App Groups&lt;/strong&gt; in the Capabilities section for both the app and widget targets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;App Groups allow the widget, which operates in an isolated container, to share data with the app.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Configure Target Membership
&lt;/h4&gt;

&lt;p&gt;To ensure the widget can interact with your app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the &lt;code&gt;QScanWidgetControl.swift&lt;/code&gt; file, add the app target to &lt;strong&gt;Target Membership&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This step is crucial for proper deep link functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Launching the Control Widget: 20 Minutes of Fun and Frustration
&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%2Fu5cjhiv193psefbcghro.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%2Fu5cjhiv193psefbcghro.jpeg" alt="Image description" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first, everything seemed to work: the widget was added to the Lock Screen and launched the app. However, the deep link didn’t work when the app was closed. The app opened but displayed the home screen, as if the deep link wasn’t passed.&lt;/p&gt;

&lt;p&gt;After some trial and error, I identified these challenges:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Deep Links and Control Widget Don’t Cooperate&lt;/strong&gt;
If the app is closed, the widget fails to correctly pass the deep link. This happens because the widget operates in an isolated container and can only trigger the app launch. The deep link data is lost during initialization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Universal Links Are Not a Solution&lt;/strong&gt;
Universal Links seemed like the next logical step, as they are often recommended for &lt;strong&gt;Control Widgets&lt;/strong&gt;. However, they had similar issues:

&lt;ul&gt;
&lt;li&gt;If the app is in the background, Universal Links work as expected and open the correct screen.&lt;/li&gt;
&lt;li&gt;If the app is closed, only the home screen is displayed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Besides, I wasn’t keen on using Universal Links, so I continued debugging to find a better approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Solution (A Bit of Magic)
&lt;/h2&gt;

&lt;p&gt;After hours of debugging, I realized the issue lay in the sequence of events:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The widget sends a request to open the deep link.&lt;/li&gt;
&lt;li&gt;iOS launches the app first.&lt;/li&gt;
&lt;li&gt;Only then does iOS pass the deep link data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this point, the app might not be fully initialized, causing the deep link request to fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But!&lt;/strong&gt; I found a workaround (it’s not perfect, but it works). By adding a small delay before triggering the deep link, the app has time to initialize, ensuring the deep link request is processed correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;
&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;PerformAction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AppIntent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;LocalizedStringResource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"displayName"&lt;/span&gt;
 &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;openAppWhenRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

 &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

 &lt;span class="kd"&gt;@MainActor&lt;/span&gt;
 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;IntentResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"your deeplink"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kt"&gt;PerformError&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invalidURL&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="kt"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;nanoseconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1_000_000_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="kt"&gt;EnvironmentValues&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;p&gt;This workaround solved the problem for me. If you have ideas for a more elegant solution, share them in the comments—we can brainstorm together!&lt;/p&gt;

&lt;p&gt;P.S. You can download and test this feature on iOS &lt;a href="https://apps.apple.com/app/apple-store/id6563143734" rel="noopener noreferrer"&gt;here&lt;/a&gt;, in the ohmygoods.app.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>ios</category>
      <category>widget</category>
      <category>mobile</category>
    </item>
    <item>
      <title>How I upgraded project from NX to the latest version 0.72.3 of React Native</title>
      <dc:creator>irinaivanovaalex</dc:creator>
      <pubDate>Sat, 26 Aug 2023 12:20:12 +0000</pubDate>
      <link>https://dev.to/irinaivanovaalex/how-i-upgraded-project-from-nx-to-the-latest-version-0723-of-react-native-15bh</link>
      <guid>https://dev.to/irinaivanovaalex/how-i-upgraded-project-from-nx-to-the-latest-version-0723-of-react-native-15bh</guid>
      <description>&lt;p&gt;Updating &lt;code&gt;react-native&lt;/code&gt; is often associated with significant challenges for developers, often consuming a considerable amount of working time and effort, especially when a project utilizes numerous libraries or possesses a non-standard structure.&lt;/p&gt;

&lt;p&gt;In this article, I will share the experiences I encountered in a project using the &lt;code&gt;NX&lt;/code&gt; monorepository and discuss what helped me overcome this issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Upgrading to the new version of React Native following the guide
&lt;/h3&gt;

&lt;p&gt;Initial dependency versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"nx": "16.4.0",
"react-native": "0.71.10",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having a monorepository, it was necessary to upgrade to the latest version of &lt;code&gt;React Native&lt;/code&gt;. Upon upgrading to version 0.72.3 using the &lt;a href="https://react-native-community.github.io/upgrade-helper/"&gt;React Native Helper&lt;/a&gt;, the projects build successfully, but we encounter an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;react-native could not be found within the project or in these directories:
  ../../node_modules
&amp;gt; 1 | import { AppRegistry } from 'react-native'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X1QvBrkD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dyw7pkhile6cz8330ax8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X1QvBrkD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dyw7pkhile6cz8330ax8.png" alt="Image description" width="720" height="960"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The issue is that &lt;code&gt;Metro&lt;/code&gt; built the project somewhat incorrectly. The build process through &lt;code&gt;Metro&lt;/code&gt; involves several stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;      &lt;code&gt;Metro&lt;/code&gt; constructs a graph of all modules.&lt;/li&gt;
&lt;li&gt;      It transpiles modules into a format understandable by &lt;code&gt;React Native&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;      Then, it serializes them, bundling the modules into one or more bundles.
However, since the project uses &lt;code&gt;NX&lt;/code&gt;, its structure differs from the basic one, resulting in distinct configuration files at various project levels.
Let's take a look at the repository structure within &lt;code&gt;NX&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;monorepo-name/
├── apps/
│   └── mobile/
│     ├── app.json
│     ├── metro.config.js
│     ├── package.json
│     ├── project.json 
│   └── mobile-e2e/
├── libs/ 
│   └── shared-ui-layout/
│       ├── src/
│       │   └── index.ts
│       ├── .babelrc
│       ├── jest.config.js
│       ├── project.json
│       ├── README.md
│       ├── test-setup.ts
│       ├── tsconfig.json
│       ├── tsconfig.lib.json
│       └── tsconfig.spec.json
├── tools/
├── babel.config.json
├── jest.config.js
├── jest.preset.js
├── nx.json
├── package-lock.json
├── package.json
└── tsconfig.base.json

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configurations Adjustment
&lt;/h3&gt;

&lt;p&gt;In this project structure, each application has its set of configuration files, such as &lt;code&gt;metro.config.js&lt;/code&gt;, &lt;code&gt;package.json&lt;/code&gt;, and others. However, besides these configurations, there are also files in the repository's root directory that need to be taken into account during the build process. &lt;code&gt;NX&lt;/code&gt; itself provides the &lt;em&gt;withNxMetro&lt;/em&gt; function, which allows merging &lt;code&gt;NX&lt;/code&gt; configurations and user-defined ones into a single configuration. As a result, to achieve the necessary configuration in the &lt;code&gt;metro.config.js&lt;/code&gt; file, we will make the following changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;      We will begin by merging the default and user-defined configurations using the &lt;em&gt;mergeConfig&lt;/em&gt; function, following the guidelines of the &lt;code&gt;React Native&lt;/code&gt; guide.&lt;/li&gt;
&lt;li&gt;      Then, we will pass the result of this merge to the &lt;em&gt;withNxMetro&lt;/em&gt; function to combine it with the &lt;code&gt;NX&lt;/code&gt; configuration.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { withNxMetro } = require('@nx/react-native')
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config')
const exclusionList = require('metro-config/src/defaults/exclusionList')

const defaultConfig = getDefaultConfig(__dirname)
const { assetExts, sourceExts } = defaultConfig.resolver

/**
 * Metro configuration
 * https://facebook.github.io/metro/docs/configuration
 *
 * @type {import('metro-config').MetroConfig}
 */
const customConfig = {
  transformer: {
    babelTransformerPath: require.resolve('react-native-svg-transformer'),
  },
  resolver: {
    assetExts: assetExts.filter(ext =&amp;gt; ext !== 'svg'),
    sourceExts: [...sourceExts, 'svg'],
    blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
    unstable_enableSymlinks: true,
    unstable_enablePackageExports: true,
  },
}

module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
  // Change this to true to see debugging info.
  // Useful if you have issues resolving modules
  debug: false,
  // all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json'
  extensions: [],
  // Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules)
  watchFolders: [],
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After such an upgrade, the project's &lt;code&gt;metro.config.js&lt;/code&gt; will launch, and we'll see the initial app screen that we've been longing to see again. 🎉&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Final library versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"nx": "16.6.0",
"react-native": "0.72.3",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  To sum up:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Follow the update guide at this &lt;a href="https://react-native-community.github.io/upgrade-helper/"&gt;link&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Upgrade library versions with &lt;code&gt;NX&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Modify the &lt;code&gt;metro.config.js&lt;/code&gt; file.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;I trust that this article provided a solution for individuals who faced this issue or are still utilizing outdated versions of &lt;code&gt;NX&lt;/code&gt; and &lt;code&gt;React Native&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/irinaivanovaalex/kak-ia-apghrieidila-proiekt-s-nx-na-posliedniuiu-viersiiu-0723-react-native-1km"&gt;Translation of this article into Russian&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nx</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>Как я апгрейдила проект с NX на последнюю версию 0.72.3 React Native</title>
      <dc:creator>irinaivanovaalex</dc:creator>
      <pubDate>Fri, 25 Aug 2023 11:05:55 +0000</pubDate>
      <link>https://dev.to/irinaivanovaalex/kak-ia-apghrieidila-proiekt-s-nx-na-posliedniuiu-viersiiu-0723-react-native-1km</link>
      <guid>https://dev.to/irinaivanovaalex/kak-ia-apghrieidila-proiekt-s-nx-na-posliedniuiu-viersiiu-0723-react-native-1km</guid>
      <description>&lt;p&gt;Обновление &lt;code&gt;react-native&lt;/code&gt; связано с большой болью у разработчиков, часто это занимает много рабочего времени и сил, особенно если проект использует большое количество библиотек или имеет нестандартную структуру. &lt;/p&gt;

&lt;p&gt;В этой статье я расскажу с чем мне пришлось столкнуться в проекте c монорепозиторием &lt;code&gt;NX&lt;/code&gt; и что мне помогло решить эту проблему.&lt;/p&gt;

&lt;h3&gt;
  
  
  Переход по гайду на новую версию React Native
&lt;/h3&gt;

&lt;p&gt;Изначальные версии зависимостей:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"nx": "16.4.0",
"react-native": "0.71.10",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Имея монорепозиторий нужно было перейти на последний &lt;code&gt;React Native&lt;/code&gt;. При переезде на 0.72.3 версию по &lt;a href="https://react-native-community.github.io/upgrade-helper/"&gt;React Native Helper&lt;/a&gt; проекты собираются, но в итоге  мы получаем ошибку:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;react-native could not be found within the project or in these directories:
  ../../node_modules
&amp;gt; 1 | import { AppRegistry } from 'react-native'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X1QvBrkD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dyw7pkhile6cz8330ax8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X1QvBrkD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dyw7pkhile6cz8330ax8.png" alt="Image description" width="720" height="960"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Проблема в том, что &lt;code&gt;Metro&lt;/code&gt; собрал проект не совсем корректно, сборка через &lt;code&gt;Metro&lt;/code&gt; проходит в несколько этапов:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Metro&lt;/code&gt; строит граф всех модулей, &lt;/li&gt;
&lt;li&gt;транспилирует модуля в формат, понятный React Native &lt;/li&gt;
&lt;li&gt;и сериализует их, объединяя модули для создания одного или нескольких bundle&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Но так как проект использует &lt;code&gt;NX&lt;/code&gt;, его структура отличается от базовой и мы имеем разные конфигурационные файлы на разных уровнях проекта. &lt;/p&gt;

&lt;p&gt;Рассмотрим структуру репозитория на &lt;code&gt;NX&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;monorepo-name/
├── apps/             // список наших приложений
│   └── mobile/
│     ├── app.json
│     ├── metro.config.js
│     ├── package.json
│     ├── project.json 
│   └── mobile-e2e/
├── libs/            // общие библиотеки, которые мы можем подключать к приложениям
│   └── shared-ui-layout/
│       ├── src/
│       │   └── index.ts
│       ├── .babelrc
│       ├── jest.config.js
│       ├── project.json
│       ├── README.md
│       ├── test-setup.ts
│       ├── tsconfig.json
│       ├── tsconfig.lib.json
│       └── tsconfig.spec.json
├── tools/
├── babel.config.json
├── jest.config.js
├── jest.preset.js
├── nx.json
├── package-lock.json
├── package.json
└── tsconfig.base.json

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Изменение конфига
&lt;/h3&gt;

&lt;p&gt;В данной структуре проекта каждое приложение имеет свой набор конфигурационных файлов, таких как &lt;code&gt;metro.config.js&lt;/code&gt;, &lt;code&gt;package.json&lt;/code&gt; и другие. Однако помимо этих конфигов, существуют также файлы в корневой директории репозитория, которые также необходимо учесть в процессе сборки. У самого &lt;code&gt;NX&lt;/code&gt; есть &lt;em&gt;withNxMetro&lt;/em&gt; - эта функция, которая позволяет объединить конфиги &lt;code&gt;NX&lt;/code&gt; и пользовательские в одну. В итоге, для достижения необходимого конфига в файле &lt;code&gt;metro.config.js&lt;/code&gt;, мы внесем следующие изменения:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Начнем со слияния дефолтных и пользовательских конфигов с помощью функции &lt;em&gt;mergeConfig&lt;/em&gt;, следуя рекомендациям из гайда &lt;code&gt;React Native&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Затем, результат этого слияния передадим в функцию &lt;em&gt;withNxMetro&lt;/em&gt; для объединения с конфигом &lt;code&gt;NX&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { withNxMetro } = require('@nx/react-native')
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config')
const exclusionList = require('metro-config/src/defaults/exclusionList')

const defaultConfig = getDefaultConfig(__dirname)
const { assetExts, sourceExts } = defaultConfig.resolver

/**
 * Metro configuration
 * https://facebook.github.io/metro/docs/configuration
 *
 * @type {import('metro-config').MetroConfig}
 */
const customConfig = {
  transformer: {
    babelTransformerPath: require.resolve('react-native-svg-transformer'),
  },
  resolver: {
    assetExts: assetExts.filter(ext =&amp;gt; ext !== 'svg'),
    sourceExts: [...sourceExts, 'svg'],
    blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
    unstable_enableSymlinks: true,
    unstable_enablePackageExports: true,
  },
}

module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
  // Change this to true to see debugging info.
  // Useful if you have issues resolving modules
  debug: false,
  // all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json'
  extensions: [],
  // Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules)
  watchFolders: [],
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;После такого апгрейда &lt;code&gt;metro.config.js&lt;/code&gt; проект запуститься и мы увидим первый экран приложения, который мы так долго хотели увидеть снова. 🎉&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Финальные весии библиотек:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"nx": "16.6.0",
"react-native": "0.72.3",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Подытожим:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Апдейтимся по &lt;a href="https://react-native-community.github.io/upgrade-helper/"&gt;гайду&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Апгрейдим версии библиотек с &lt;code&gt;NX&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Меняем &lt;code&gt;metro.config.js&lt;/code&gt; &lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Заключение
&lt;/h3&gt;

&lt;p&gt;Надеюсь, что это статья помогла решить проблему тех, кто столкнулся с такой ошибкой или все еще сидит на старых версиях &lt;code&gt;NX&lt;/code&gt; и &lt;code&gt;React Native&lt;/code&gt;.&lt;br&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>nx</category>
    </item>
  </channel>
</rss>
