Question about upgrading Expo SDK and ExpoKit

Please provide the following:

  1. SDK Version: 33.0.0
  2. Platforms(Android/iOS/web/all): iOS and Android

Hi all,

Since I’ve been developing my application using Expo’s Managed Workflow, I’ve been using SDK v33. However, I would like to upgrade to v36 as it uses React Native v0.60+. This means that Autolinking will be supported and manual linking of packages will not be required anymore.

As I’m reading through the docs to upgrade, there’s a section noted “Updating ExpoKit to SDK 34” – which links to here.

Now, when following the directions in the documentation, I’m instructed to open the ios/Podfile in my project. I don’t have this directory in my project, so how do I open up the file?

Thanks for the help!

Hi

ExpoKit is not the same as the managed workflow. ExpoKit is one form of ejecting. Could you clarify your comment about manual linking of packages? This is not required and indeed not possible with the managed workflow.

To upgrade a managed workflow app from SDK 33 to SDK 34 (and later 35 and 36) follow the instructions in the release notes for each SDK version, ignoring the “Updating ExpoKit” sections:

SDK 34 release notes

Follow the Upgrading Your App section. Check the Breaking Changes section for things you might need to change. Update the libraries mentioned in the Library Updates if you have them installed. You can do this by running expo install name-of-the-library.

In SDK 33 a bunch of import statements needed to be changed. There are more of these in later SDK versions, so you’ll need to watch out for that and check the docs for the modules you use if you get errors like “… is not a function”.

If you use other third party dependencies you might need to upgrade them as well.

SDK 35 release notes

The process is basically the same for all upgrades. Follow the Upgrading instructions. Fix anything mentioned in Breaking Changes. Upgrade libraries mentioned in Library Updates. Some of this work is now automated with the expo upgrade command.

SDK 36 release notes

Again the process is basically the same. From SDK 36 onwards you’ll need to check the changelog for the breaking changes and library updates.

Hi and thanks for the reply wodin.

Then I believe that I’m using an Expo managed app and not using ExpoKit, since I’m referencing the API in my app/code, using Expo CLI, and publishing to Expo. I’ve never ejected to a bare workflow by the way. Therefore, I assume that I’m free to update the SDK and not ExpoKit?

Regarding your comment about manual linking of packages: When using different libraries, I’m coming across installation instructions in the “Getting Started” section, which looks like the following.

Many installation instructions note that if we are using React Native < 0.60, then we need to run the command react-native link @react-native-community/netinfo after running npm install --save @react-native-community/netinfo.

Currently, I’m unable to use libraries that require me to use react-native link. This is my reasoning for upgrading to SDK 36 – so that I can have access to React Native v0.61 and Autolinking will work. Or is my reasoning wrong and there’s another way to approach this?

Thanks in advance!

Yes, as I said, ExpoKit is a form of ejecting. i.e. you could eject to the Bare Workflow or to ExpoKit. ExpoKit is currently not recommended. So you should follow the normal upgrade process and ignore the Updating Expokit sections as per my previous comment.

Unfortunately I don’t believe Autolinking helps you to use libraries that are not included in Expo. You would still need to eject to the bare workflow in order to use them. You just would not have to run the “react-native link” command after ejecting.

But if you want to share stuff, the following might work for you:

https://docs.expo.io/versions/v36.0.0/tutorial/sharing/
https://docs.expo.io/versions/v36.0.0/sdk/sharing/

And @react-native-community/netinfo is included in Expo already:

https://docs.expo.io/versions/v36.0.0/sdk/netinfo/

See also the Network module:

https://docs.expo.io/versions/v36.0.0/sdk/network/

If you want to use things that are not included in SDK 36 you will still need to eject, unfortunately.

Ah understood–thanks for the clarification! So I successfully upgraded to the latest version, SDK v36. However, now I’m experiencing the following issue.

When I run the command expo start, I get the following error. Unfortunately, I’m not able to get the project running on the iOS simulator or the Expo mobile app.

Also, a couple of things to add. Running react-native --help does not work and setting EXPO_DEBUG=true does nothing as I cannot see the stack trace. The problem continues to happen even after I close my current terminal session and open a new one.

hmmm… Did you by any chance eject? Given what you’ve told me so far it doesn’t look like you would need to.

What does your app.json contain? Please paste it here, but rather as text and mark it as a code block/preformatted text. You can obscure anything you don’t want to share like API keys etc.

Also run expo diagnostics from the root of your project and include that as well, please.

Thanks again for your help! I haven’t ejected from Expo at the moment.

Here’s my app.json file:

{
  "expo": {
    "name": "[--Removed--]",
    "slug": "[--Removed--]",
    "privacy": "unlisted",
    "sdkVersion": "36.0.0",
    "platforms": [
      "ios",
      "android",
      "web"
    ],
    "version": "2.0.1",
    "orientation": "portrait",
    "icon": "./assets/images/icon.png",
    "facebookScheme": "fb742285009531350",
    "splash": {
      "image": "./assets/images/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "updates": {
      "fallbackToCacheTimeout": 0
    },
    "assetBundlePatterns": [
      "**/*"
    ],
    "ios": {
      "supportsTablet": false,
      "bundleIdentifier": "[--Removed--]"
    }
  }
}

Here’s the output when I run expo diagnostics

  Expo CLI 3.11.7 environment info:
    System:
      OS: macOS High Sierra 10.13.6
      Shell: 3.2.57 - /bin/bash
    Binaries:
      Node: 10.15.3 - /usr/local/bin/node
      Yarn: 1.21.1 - /usr/local/bin/yarn
      npm: 6.4.1 - /usr/local/bin/npm
      Watchman: 4.9.0 - /usr/local/bin/watchman
    IDEs:
      Android Studio: 3.4 AI-183.6156.11.34.5522156
      Xcode: 10.1/10B61 - /usr/bin/xcodebuild
    npmPackages:
      expo: ^36.0.0 => 36.0.2 
      react: 16.9.0 => 16.9.0 
      react-native: https://github.com/expo/react-native/archive/sdk-36.0.1.tar.gz => 0.61.4 
      react-navigation: ^3.11.0 => 3.11.0 
    npmGlobalPackages:
      expo-cli: 3.11.7

Sorry, can you paste in your package.json too please?

For good measure you may as well also upgrade to the latest version of expo-cli.

Here’s my package.json file.

{
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject",
    "test": "jest --watchAll"
  },
  "jest": {
    "preset": "jest-expo"
  },
  "dependencies": {
    "@expo/react-native-action-sheet": "^3.0.3",
    "@expo/samples": "~3.0.0",
    "@expo/vector-icons": "^10.0.0",
    "@react-native-community/netinfo": "4.6.0",
    "apollo-boost": "^0.4.2",
    "axios": "^0.19.0",
    "debug": "^4.1.1",
    "expo": "^36.0.0",
    "expo-asset": "~8.0.0",
    "expo-cli": "^3.11.2",
    "expo-constants": "~8.0.0",
    "expo-facebook": "~8.0.0",
    "expo-font": "~8.0.0",
    "expo-haptics": "~8.0.0",
    "expo-image-picker": "~8.0.1",
    "expo-linear-gradient": "~8.0.0",
    "expo-mail-composer": "~8.0.0",
    "expo-permissions": "~8.0.0",
    "expo-sms": "~8.0.0",
    "expo-web-browser": "~8.0.0",
    "firebase": "^6.1.1",
    "lottie-ios": "^3.0.3",
    "lottie-react-native": "~2.6.1",
    "moment": "^2.24.0",
    "react": "16.9.0",
    "react-apollo": "^2.5.6",
    "react-dom": "16.9.0",
    "react-native": "https://github.com/expo/react-native/archive/sdk-36.0.1.tar.gz",
    "react-native-geocoding": "^0.3.0",
    "react-native-gesture-handler": "~1.5.0",
    "react-native-google-places-autocomplete": "^1.3.9",
    "react-native-haptic": "^1.0.1",
    "react-native-haptic-feedback": "^1.8.2",
    "react-native-keyboard-accessory": "^0.1.9",
    "react-native-keyboard-aware-scroll-view": "^0.9.1",
    "react-native-maps": "0.26.1",
    "react-native-modal-datetime-picker": "^7.5.0",
    "react-native-modalbox": "^2.0.0",
    "react-native-open-maps": "^0.3.3",
    "react-native-payments": "^0.7.1",
    "react-native-read-more-text": "^1.1.0",
    "react-native-reanimated": "~1.4.0",
    "react-native-share": "^1.2.1",
    "react-native-simple-dialogs": "^1.2.0",
    "react-native-tab-view": "^2.10.0",
    "react-native-touchable-scale": "^2.0.0",
    "react-native-vector-icons": "^6.5.0",
    "react-native-view-more-text": "^2.1.0",
    "react-native-web": "^0.11.7",
    "react-navigation": "^3.11.0",
    "react-redux": "^7.1.0",
    "redux": "^4.0.4",
    "redux-persist": "^5.10.0",
    "redux-thunk": "^2.3.0",
    "styled-components": "^4.3.1",
    "react-native-svg": "9.13.3"
  },
  "devDependencies": {
    "babel-preset-expo": "^8.0.0",
    "jest-expo": "^36.0.0"
  },
  "private": true,
  "rnpm": {
    "assets": [
      "assets/fonts"
    ]
  }
}

Here’s the output when I update to the latest version of expo-cli. Unfortunately, I still get the same error when I run expo start.

OK, that’s weird. You have a much larger than expected set of dependencies in your package.json. Are you really using all of those? I haven’t checked to see whether they are all compatible with Expo, but was it working with those dependencies before the upgrade?

What I would do if I were you is this:

Create a new project using expo init and compare the differences between the two in terms of the app.json and package.json. Then try installing one dependency at a time into the new app to see if/when it breaks. Once you have all the dependencies you need, start copying the code across.

By doing the above you should be able to figure out what part is causing the problem and we’ll have a better idea of how to fix it.

Gotcha–understood.

I’m using about 80% of the dependancies in the package.json. For the ones that I’m not using, can I just delete them here? Will they be deleted from the project or do I have to delete them the way that I had installed them at the command line?

Now, I ran the command npm start and this is the output that I received. I’m having a bit of trouble parsing the (node: 15901) part. Do you think that this extra info will help us pinpoint and debug where the issue is?

Thanks!

OK, so the dependencies are handled by either yarn or npm.

If you previously used yarn to install dependencies you will have a yarn.lock file in the root of your project.
If you previously used npm to install dependencies you will have a package-lock.json file.
If you have both you should rather decide whether you want to use npm or yarn for installing your dependencies and stick to that. They both do basically the same thing although some people prefer one over the other.

Note: Both yarn and npm can manage things specific to this particular app and stuff that’s more generally applicable (global). It’s fine to e.g. install global stuff (like expo-cli) with one tool and install your app’s dependencies (the stuff listed in package.json) with the other tool.

e.g. if you want to use yarn for managing your app’s dependencies, remove package-lock.json and run yarn. Then you can run yarn remove package-name to remove the package-name dependency.

(For npm it would be remove yarn.lock and run npm install followed by npm uninstall package-name.)

Based on your error messages I think there might be something weird going on with Metro. You should not be getting any messages about react-native --help (or react-native at all).

I think start by uninstalling the dependencies you don’t need. Then if that still doesn’t fix it maybe try creating a new project and adding dependencies to it one by one (e.g. yarn add blah) to see if one of them is problematic.

Thanks for your continued support.

Yes, I did use npm to install dependencies as I do have a package-lock json. I also have a .package-lock 2.json.icloud and yarn-error.log file. Should I delete this yarn-error.log file since I’m not using yarn?

Following your suggestion, I deleted the dependencies that I’m not using. Interestingly, with each uninstalls, I have 8 vulnerabilities and one of them is high. Could this perhaps be the source of the issue as well? If so, how do I fix this? I tried to run npm audit fix and that doesn’t seem to work.

Here’s the output when I run npm audit:

Hi. No, the audit issues are not the cause of the problems.

As you can see, the ones in the image are dependencies of metro. This is the bundler that bundles your JavaScript together. It is only used while building/publishing your app and during development. So this vulnerable code is not used by your app.

EDIT: And feel free to delete the yarn-error.log.

Hey wodin – thanks again for the help!

I followed your suggestion and created a new project using expo init. I re-installed the dependencies and copied the code over. Now, the project works well and is running with Expo.

However, I’m getting this error now. I’m wondering if it has to do with the Facebook SDK from Expo. After comparing the version of both the old project and the new one, they are the same. Do you have any idea what may be the case here?

hmmm…

After poking around in the code I believe those references to “facebook” are because Facebook wrote React Native and they put that part of the code into a “facebook::React” namespace as a result. So this has nothing to do with accessing facebook.com.

Do you get any related error messages in the Terminal where you’re running expo start?

I haven’t figured out how the code gets from JSIExecutor::defaultTimeoutInvoker to line 121 of ABI36_0_0RCTConvert.m (which is in an NSURL method) unfortunately, but that NSURL code has to do with parsing a URL or path to a file and based on the variable names and the error message it seems to be trying to parse some JSON as a URL or file path, but getting null instead of a valid URL/path.

I also can’t tell what JSON is being referred to and why it would be null.

Can you try pressing the “Copy” button at the bottom of the error, which should copy the error details to the clipboard on your computer. You can then paste that into a comment here. Please paste as text like you did for your app.json etc.

Also see if the Extra Info button gives you anything useful.

Hey wodin – there are no error messages being displayed in terminal when I run expo start.

After doing some debugging, I realized that the error was coming from a function doing an API get request using Axios (hence the possibility of your NSURL observation). Interestingly, that function is very similar to another function doing a very similar API get request. Also, all the values from the database we are pulling from have no null values, so I’m confused as to why I’m getting this error.

Also, here’s the copied code for the error details:

JSON value '<null>' of type NSNull cannot be converted to a valid URL

ABI36_0_0facebook::ABI36_0_0React::JSIExecutor::defaultTimeoutInvoker(std::__1::function<void ()> const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ()>)
    ABI36_0_0RCTConvert.m:121
ABI36_0_0facebook::ABI36_0_0React::JSIExecutor::defaultTimeoutInvoker(std::__1::function<void ()> const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ()>)
ABI36_0_0facebook::ABI36_0_0React::JSIExecutor::defaultTimeoutInvoker(std::__1::function<void ()> const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ()>)
ABI36_0_0facebook::ABI36_0_0React::JSIExecutor::defaultTimeoutInvoker(std::__1::function<void ()> const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ()>)
1B9B1E61-8CB4-3903-9870-402C3DE959BB
1B9B1E61-8CB4-3903-9870-402C3DE959BB
ABI36_0_0facebook::ABI36_0_0React::JSIExecutor::defaultTimeoutInvoker(std::__1::function<void ()> const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ()>)
ABI36_0_0facebook::ABI36_0_0React::JSIExecutor::defaultTimeoutInvoker(std::__1::function<void ()> const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ()>)
ABI36_0_0facebook::ABI36_0_0React::JSIExecutor::defaultTimeoutInvoker(std::__1::function<void ()> const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ()>)
ABI36_0_0facebook::ABI36_0_0React::JSIExecutor::defaultTimeoutInvoker(std::__1::function<void ()> const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ()>)
1B9B1E61-8CB4-3903-9870-402C3DE959BB
1B9B1E61-8CB4-3903-9870-402C3DE959BB
ABI36_0_0facebook::ABI36_0_0React::JSIExecutor::defaultTimeoutInvoker(std::__1::function<void ()> const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ()>)
ABI36_0_0facebook::ABI36_0_0React::JSIExecutor::defaultTimeoutInvoker(std::__1::function<void ()> const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ()>)
ABI36_0_0facebook::ABI36_0_0React::JSIExecutor::defaultTimeoutInvoker(std::__1::function<void ()> const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ()>)
A3849F96-1C9F-36C5-A15F-70C566F14CFF
A3849F96-1C9F-36C5-A15F-70C566F14CFF
A3849F96-1C9F-36C5-A15F-70C566F14CFF
1B9B1E61-8CB4-3903-9870-402C3DE959BB
1B9B1E61-8CB4-3903-9870-402C3DE959BB
CFRunLoopRunSpecific
GSEventRunModal
UIApplicationMain
Exponent
2E3F4750-8B67-398B-8530-8417651B1718

By the way, here’s where the error is being generated:

https://github.com/expo/expo/blob/master/ios/versioned-react-native/ABI36_0_0/ReactNative/React/Base/ABI36_0_0RCTConvert.m#L121

(Assuming the line numbers in your stack trace are correct, but if not there then on line 116.)

ABI36_0_0RCTLogConvertError is defined here:

https://github.com/expo/expo/blob/master/ios/versioned-react-native/ABI36_0_0/ReactNative/React/Base/ABI36_0_0RCTConvert.h#L172-L174

Grepping for calls to this NSURL method turns up the following stuff. Maybe one of them will ring a bell?

ios/Exponent/Versioned/Core/Api/Components/WebView/RNCWebView.m:        NSURL *baseURL = [RCTConvert NSURL:_source[@"baseUrl"]];
ios/Exponent/Versioned/Core/Api/Components/WebView/RNCWebView.m:        NSURL* readAccessUrl = _allowingReadAccessToURL ? [RCTConvert NSURL:_allowingReadAccessToURL] : request.URL;
ios/versioned-react-native/ABI36_0_0/Expo/ExpoKit/Core/Api/Components/WebView/ABI36_0_0RNCWebView.m:        NSURL *baseURL = [ABI36_0_0RCTConvert NSURL:_source[@"baseUrl"]];
ios/versioned-react-native/ABI36_0_0/Expo/ExpoKit/Core/Api/Components/WebView/ABI36_0_0RNCWebView.m:        NSURL* readAccessUrl = _allowingReadAccessToURL ? [ABI36_0_0RCTConvert NSURL:_allowingReadAccessToURL] : request.URL;
ios/versioned-react-native/ABI36_0_0/ReactNative/Libraries/ActionSheetIOS/ABI36_0_0RCTActionSheetManager.m:  NSURL *URL = [ABI36_0_0RCTConvert NSURL:options[@"url"]];
ios/versioned-react-native/ABI36_0_0/ReactNative/Libraries/Network/ABI36_0_0RCTNetworking.mm:  NSURL *URL = [ABI36_0_0RCTConvert NSURL:query[@"url"]]; // this is marked as nullable in JS, but should not be null
ios/versioned-react-native/ABI36_0_0/ReactNative/Libraries/WebSocket/ABI36_0_0RCTWebSocketExecutor.m:    _url = [ABI36_0_0RCTConvert NSURL:URLString];
ios/versioned-react-native/ABI36_0_0/ReactNative/React/Base/ABI36_0_0RCTBridge.m:  _bundleURL = [ABI36_0_0RCTConvert NSURL:_bundleURL.absoluteString];
ios/versioned-react-native/ABI36_0_0/ReactNative/React/Base/ABI36_0_0RCTConvert.m:    NSURL *URL = [self NSURL:json];
ios/versioned-react-native/ABI36_0_0/ReactNative/React/Base/ABI36_0_0RCTConvert.m:    URL = [self NSURL:URLString];
ios/versioned-react-native/ABI36_0_0/ReactNative/React/Base/ABI36_0_0RCTConvert.m:  NSURL *fileURL = [self NSURL:json];
ios/versioned-react-native/ABI36_0_0/ReactNative/React/Base/ABI36_0_0RCTJavaScriptLoader.mm:  return [ABI36_0_0RCTConvert NSURL:url.absoluteString];

e.g. are you using a WebView anywhere? Or an ActionSheet? Or Networking? Or a WebSocket?