expo / expo

An open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.
https://docs.expo.dev
MIT License
34.06k stars 5.45k forks source link

[Root Cause Already Identified] - Expo App with expo-dev-client resulting in error "There was a problem loading the project" #30821

Open manganapathy opened 3 months ago

manganapathy commented 3 months ago

Summary

Issue: Expo App with expo-dev-client resulting in error "There was a problem loading the project"[Firewall Related]

Repro Steps: Note: This issue happens only in machines which has firewall rules blocking incoming requests from unknown host[NetworkAdapter/Router assigned IP to Macbook) [Certain enterprises have this rule as part of security policy]

  1. Dev Machine(Macbook/Linux Machine) with firewall rule that blocks unknown domain/ip
  2. Create a expo app with the command npx create-expo-app@latest
  3. Install expo dev client using npx expo install expo-dev-client
  4. build and run the app using the command npx expo run:ios

Expected: App should build and run fine with dev-client

Actual: App throws "There was a problem loading the project. Network connection was lost"

Screenshot 2024-08-05 at 9 54 37 PM

Managed or bare workflow?

managed

What platform(s) does this occur on?

iOS

Package versions

"expo": "~51.0.24", "expo-dev-client": "~4.0.21", "expo-constants": "~16.0.2", "expo-font": "~12.0.9", "expo-linking": "~6.3.1", "expo-router": "~3.5.20", "expo-splash-screen": "~0.27.5", "expo-status-bar": "~1.12.1", "expo-system-ui": "~3.0.7"

Environment

System: OS: macOS 14.5 Shell: 5.9 - /bin/zsh Binaries: Node: 20.15.0 - ~/.nvm/versions/node/v20.15.0/bin/node Yarn: 1.22.22 - /opt/homebrew/bin/yarn npm: 10.7.0 - ~/.nvm/versions/node/v20.15.0/bin/npm Watchman: 2024.07.15.00 - /opt/homebrew/bin/watchman Managers: CocoaPods: 1.15.2 - ~/.rvm/gems/ruby-3.1.4/bin/pod SDKs: iOS SDK: Platforms: DriverKit 23.2, iOS 17.2, macOS 14.2, tvOS 17.2, visionOS 1.0, watchOS 10.2 IDEs: Android Studio: 2024.1 AI-241.15989.150.2411.11948838 Xcode: 15.2/15C500b - /usr/bin/xcodebuild Expo Workflow: managed

Reproducible demo

Well, It's tough to produce a reproducible demo for this issue because this issue happens only in Macbooks with Firewall rules enabled to block unknown IPs. I am not able to use Tunnel because ngrok is also not allowed per corporate policies.

Though it's hard to repro this issue with firewall, I have analyzed the expo run:ios command flow and found out why cli is failing with firewall enabled where it could actually work smooth like other scenarios(expo --start)

Root Cause: npx expo run:ios command after pre-building and installing native dependencies, start the metro server Issue is while starting from run command, it passes only the "scheme" property in options object to create URL in below line startBundler.ts

Whereas two functions in 'start' command from file UrlCreator.tsdepends on either 'hostname' or 'hostType' to determine the hostname of dev server URL.

getUrlComponents(options: CreateURLOptions) getDefaultHostname(options: Pick<CreateURLOptions, 'hostname'>)

Unfortunately, since 'hostname' & 'hostType' is not passed in expo run:ios command and also only scheme is passed into start flow, expo cli always resolves to IP Address as 'hostname' & 'hostType' is undefined. This results in expo cli always resolving to exp+xxxxxxxxxx://expo-development-client/?url=http%3A%2F%2FXX.XX.X.XXX%3A8081 instead of localhost. As it's resolving to host machine IP, it's get blocked by Firewall.

Possible solutions: Solution 1 Well, Tunneling could solve this problem by exposing this IP public and access it. But as I mentioned earlier, if Tunneling is not allowed as per above reasons, I am proposing below solution.

Provide --host as a optional flag to expo run command like expo run:ios --host localhost

Proposed solution is similar to what expo --start -m, --host already has which makes it explicit to select the host for running the app.

This solution can help use cases where expo run:ios is blocked with firewall issues and avoid using 2 commands start with tunnel and again run to start the app.

Solution 2 Downside of above solution is that it deviates away from react-native-cli run-ios command which doesn't have --host option.To mitigate that we can opt for Solution2

In getDefaultHostname(options: Pick<CreateURLOptions, 'hostname'>) before we fallback to assign IP Address of the current device, we can choose to do one more additional check of is localhost currently running in the current machine(Which will be likely true if running from computer) and if the device is simulator. This also safe because even if developer want to choose a different host, it can be still done from dev-client launcher screen.

This similar to what react-native-community-cli already has https://github.com/react-native-community/cli/blob/6a61d5dcc99c6430b74c1095e05ffb315909b078/packages/cli-tools/src/isPackagerRunning.ts#L18

Solution 3(Recommended) EXDevLauncherErrorViewController currently displays full screen error message in case of any error with loading app with given URL.

I suggest we can change the full screen error to a snack bar message and displays it in dev-launcher home screen because it already has lot of fallback options

  1. Fetch development servers
  2. Enter URL manually

It is safe to fallback to snack bar instead of full screen so that users can choose from the given options to connect to desired host

Current Workaround: There is another workaround I found to be working is setting EXPO_PACKAGER_PROXY_URL. But I saw a comment that is deprecated and I am little skeptical to use.

Stacktrace (if a crash is involved)

No response

keith-kurak commented 3 months ago

Hi @manganapathy- these issues can be a little tricky for us to troubleshoot since we can't replicate your network environment. Being able to connect a computer to a device over the network is a pretty critical workflow for mobile development, so you may want to engage your network security team to come up with a better solution than blocking everything. For instance, could they assign static IP's to the mobile devices and then allowlist those IP's?

I was wondering if you could clarify how resolving to localhost would cover this. As I understand, this would work for iOS simulators, but it wouldn't work for Android emulators (which use 10.10.2.2 I believe as a proxy for localhost) or devices, since localhost relative to your device would be itself, and not the dev server.

Speaking of your current workaround, I was wondering if REACT_NATIVE_PACKAGER_HOSTNAME environment variable might work for you. That should output a different URL on the command line and isn't deprecated.

manganapathy commented 2 months ago

Thanks @keith-kurak. I have initiated conversation with my network security team.

Regarding workaround, would be happy to try using REACT_NATIVE_PACKAGER_HOSTNAME. But again double checking with you on this line 148 on UrlCreator.ts that has TODO comment to drop this env variable too. Hope I can ignore it for now.

manganapathy commented 2 months ago

My intention for filing this issue is to have expo run:ios resolve to Host Machine IP only if all below conditions are true .i.e user is running on a device instead of simulator. iOS simulators with Debug mostly points to localhost

  1. Make sure --no-bundler flag is NOT passed => i.e user requesting bundle using Metro
  2. Make sure configuration is 'Debug' => Debug builds mostly(99% ??) connect to metro for JS bundle
  3. Requested device is not Simulator

React Native already does this in Xcode build script(packages/react-native/scripts/react-native-xcode.sh) but I guess it's overwritten inside expo cli in UrlCreator.ts There could be a valid use case but curious to know about it

manganapathy commented 2 months ago

Running expo run:ios with REACT_NATIVE_PACKAGER_HOSTNAME fixes the issue with localhost + simulator issue. But I still have issue with device flow + Tunnel + opening Chrome Dev Tools https://github.com/expo/expo/issues/31287

pandoraxcc commented 3 weeks ago

I got similar problem after recent OS/Xcode updates:

"

There was a problem loading the requested app.

The network connection was lost. It looks like you may be using a LAN URL. Make sure your device is on the same network as the server, and that you have granted Expo Go the Local Network permission in the Settings app, or try using the tunnel connection type.

"

If you use simulator from Xcode to debug, then disabling the connection on Mac solves the issue.

However the same network setup and connected Mac works in a tunnel mode with no issues. (expo@51.0.36)