React Native + Expo + Android + HTTP Server = Network request failed

I am trying to use the fetch API in order to upload in image to a remote server.
The server address does not use HTTPS protocol, only HTTP. I can’t change that.

I’ve read tons of threads on SO and GitHub but I can’t figure who is the culprit: React-Native, Expo, fetch API or the HTTP Server.

I’ve read that I need to set cleartextTrafficPermitted="true" in androidManifest. But I am using Expo in my project so there are no manifest files.

Where do I set the permission needed to POST images to a HTTP server for both Android and iOS ?

I am using EXPO GO on an Android phone for development: Client version: 2.29.6

package.json:

  "dependencies": {
    "@expo/vector-icons": "^13.0.0",
    "@expo/webpack-config": "^19.0.0",
    "expo": "~49.0.8",
    "expo-camera": "~13.4.2",
    "expo-constants": "~14.4.2",
    "expo-image": "~1.3.2",
    "expo-image-manipulator": "~11.3.0",
    "expo-image-picker": "~14.3.2",
    "expo-linking": "~5.0.2",
    "expo-router": "2.0.0",
    "expo-status-bar": "~1.6.0",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-native": "0.72.4",
    "react-native-animated-spinkit": "^1.5.2",
    "react-native-gesture-handler": "~2.12.0",
    "react-native-loading-spinner-overlay": "^3.0.1",
    "react-native-safe-area-context": "4.6.3",
    "react-native-screens": "~3.22.0",
    "react-native-web": "~0.19.6",
    "expo-file-system": "~15.4.4"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "babel-plugin-module-resolver": "^5.0.0",
    "prettier": "^3.0.2"
  },

app.json:

{
  "expo": {
    "name": "lpr-android",
    "slug": "lpr-android",
    "version": "1.0.0",
    "scheme": "lpr",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "light",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "assetBundlePatterns": [
      "**/*"
    ],
    "ios": {
      "supportsTablet": true
    },
    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      }
    },
    "web": {
      "favicon": "./assets/favicon.png"
    },
    "plugins": [
      [
        "expo-camera",
        {
          "cameraPermission": "Allow $(PRODUCT_NAME) to access your camera."
        }
      ],
      [
        "expo-image-picker",
        {
          "photosPermission": "Allow $(PRODUCT_NAME) to access your image gallery."
        }
      ],
      "expo-router"
    ]
  }
}

This is how I use fetch:

const formData = new FormData();

const img = {
uri: compressedImage.uri,
name: compressedImage.uri.split("/").pop(),
type: "image/jpeg",
};

// Append the image to the FormData object with a specified field name
formData.append("image", img);

fetch(HTTP_SERVER_URL, {
method: "POST",
headers: {
  Accept: "application/json",
  "Content-Type": "multipart/form-data",
},
body: formData,
})
.then((response) => response.json())
.then((data) => {
  if (data.error === true) throw new Error(JSON.stringify(data));
  setResponse(JSON.stringify(data.response.body));
})
.catch((error) => {
  console.log("Error:", error.message);
  setError(error.message);
});

where

compressedImage {"base64": null, "height": 1067, "uri": "file:///data/user/0/host.exp.exponent/cache/ImageManipulator/0390fb75-3752-418c-a8c7-5e8837af099e.jpg", "width": 800}

Hi @georgeflorian

EDIT: Expand old answer about writing your old config plugin

For this sort of thing, you need a config plugin.

Here’s one I wrote a while ago:

But with Expo SDK >= 47 you should change this:

const { withAndroidManifest } = require("@expo/config-plugins");

to this:

const { withAndroidManifest } = require("expo/config-plugins");

Then, you use it something like this:

Except, in your case you’d want true instead of false.


These days you can do this in BuildProperties:

For iOS, you’d need something like this:

And you can add the Info.plist changes to app.json. Something like this (based on the above answer. I haven’t tested it):

{
  "expo": {
    "ios": {
      "infoPlist": {
        "NSAppTransportSecurity": {
          "NSExceptionDomains": {
            "example.com": {
              // Include to allow subdomains
              "NSIncludesSubdomains": true,
              // Include to allow HTTP requests
              "NSTemporaryExceptionAllowsInsecureHTTPLoads": true,
              // Include to specify minimum TLS version
              "NSTemporaryExceptionMinimumTLSVersion": "TLSv1.1"
            }
          }
        }
      }
    }
  }
}

hey @wodin , my issue is not ressolved yet, with the above solution. I tried with all the things, but still getting the issue.

I bumped from expo 48 to expo 49 and now run into the same issue
Adding it to “app.json” solved the issue (see release note for EXPO 49 for more details) :

    "plugins": [
      [
        "expo-build-properties",
        {
          "android": {
            "usesCleartextTraffic": true
          }
        }
      ]
    ]
2 Likes

@iammayank20 I have just noticed, and I see that @vanmalleghema did too, that you can enable this with BuildProperties now.

This worked. Thank you very much for the solution.
They should add this to the documentation.

It’s in the Expo BuildProperties documentation

Okay. Then they could add a note saying what issues it can fix.

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.