13

I'm testing a react native app (on OS X Yosemite in the xcode simulator v9.2 / xcode 7.2.1). I'm getting a Network request failed error with the below code. The actual url with the correct appid works just fine in a browser and gives me the correct information in json format, and the promise / api call looks fine.

I'm not behind a firewall. I've tried troubleshooting the connection, and activating the Allow HTTP Services in the Developer settings, but I'm still getting the error.

Any idea what the problem is here? The actual errors are as follows:

-- There has been a problem with your fetch operation: Network request failed
-- Api call error =  Network request failed

Here's the api.js code:

var _ = require('lodash');
var rootUrl = 'http://api.openweathermap.org/data/2.5/weather?APPID=xxxxxxxxxxxxxxxxxxxxxx';

var kelvinToF = function(kelvin) {
  return Math.round((kelvin - 273.15) * 1.8 + 32) + ' ˚F'
};

var kelvinToC = function(kelvin) {
  return Math.round(kelvin - 273.15) + ' ˚C'
};  

module.exports = function(latitude, longitude) {
  var url = `${rootUrl}&lat=${latitude}&lon=${longitude}`;
  console.log(url);
  return fetch(url)
    .then(function(response){
      return response.json();
    })
    .then(function(json){
      return {
        city: json.name,
        temperature1: kelvinToF(json.main.temp),
        temperature2: kelvinToC(json.main.temp),
        description: _.capitalize(json.weather[0].description)
      }
    })
    .catch(function(error) {
    console.log('There has been a problem with your fetch operation: ' + error.message);
    throw error;
});
}

Here's the index.ios.js code.

/*  --depreciated

var React = require('react-native'); 

var {
  AppRegistry,
  MapView,
  View,
  Text,
  StyleSheet
} = React;
*/ 


// updated
import React from 'react';

// updated
import {
  AppRegistry,
  MapView,
  View,
  Text,
  StyleSheet,
} from 'react-native';

var Api = require('./src/api');

var Weather = React.createClass({
  getInitialState: function() {
    return {
      pin: {
        latitude: 0,
        longitude: 0
      },
      city: '',
      temperature1: '',
      temperature2: '',
      description: ''
    };
  },
  render: function() {
    return <View style={styles.container}>
      <MapView
        annotations={[this.state.pin]}
        onRegionChangeComplete={this.onRegionChangeComplete}
        style={styles.map}>
      </MapView>
      <View style={styles.textWrapper}>
        <Text style={styles.text}>{this.state.city}</Text>
        <Text style={styles.text}>{this.state.temperature1}</Text>
        <Text style={styles.text}>{this.state.temperature2}</Text>
        <Text style={styles.text}>{this.state.description}</Text>
      </View>
    </View>
  },
  onRegionChangeComplete: function(region) {
    this.setState({
      pin: {
        longitude: region.longitude,
        latitude: region.latitude
      }
    });

    Api(region.latitude, region.longitude)
      .then((data) => {
        console.log(region.latitude);
        console.log(region.longitude);
        console.log('data = ' + data);
        this.setState(data);
      })
      .catch((error)=>{
     console.log("Api call error = ", error.message);
    // alert(error.message);     
  });
  }
});


var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'stretch',
    backgroundColor: '#F5FCFF'
  },
  map: {
    flex: 2,
    marginTop: 30
  },
  textWrapper: {
    flex: 1,
    alignItems: 'center'
  },
  text: {
    fontSize: 30
  }
});

AppRegistry.registerComponent('weather', () => Weather);
Agent Zebra
  • 4,410
  • 6
  • 33
  • 66

4 Answers4

25

Ok thanks to @while1's answer above, I found the answer that worked for me.

The code in my question above is fine. I had to change the info.plist file.

Wow what a pain in the ass apple are, why would they complicate things so extremely?

I basically added the following to the info.plist file.

<key>NSAllowsArbitraryLoads</key>
<true/> 

Like this:

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
        <key>NSExceptionDomains</key>
        <dict>
            <key>localhost</key>
            <dict>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
        </dict>
    </dict>

See more info on this in the link in @while1's answer above.

Ruvee
  • 8,611
  • 4
  • 18
  • 44
Agent Zebra
  • 4,410
  • 6
  • 33
  • 66
12

You will need to change your NSAppTransportSecurity policy in info.plist. By default in ios 8 and greater plaintext request are blocked. See Transport security has blocked a cleartext HTTP

Community
  • 1
  • 1
while1
  • 3,320
  • 19
  • 12
1

your code looks familiar to me seems you're learning Build apps with react native course offered by Stephen Grider.

Even i had this issue.

include delete GLOBAL.XMLHttpRequest; at the beginning of api.js file.

Like this:

delete GLOBAL.XMLHttpRequest;
var _ = require('lodash');
var rootUrl = 'http://api.openweathermap.org/data/2.5/weather?APPID=xxxxxxxxxxxxxxxxxxxxxx';
Ashok R
  • 19,892
  • 8
  • 68
  • 68
  • Haha you're right, yes this is from that course. What is the GLOBAL.XMLHttpRequest ? I tried what you suggested, i'm now getting the error ` XMLHttpRequest is not defined' Specifically `There has been a problem with your fetch operation: XMLHttpRequest is not defined` and `Api call error = XMLHttpRequest is not defined` Thank you Ashok. – Agent Zebra Aug 10 '16 at 18:56
0

In my case I was using a real device specific (IOS), and a local backend running, just changing the URL of the endpoint in the app to use the private local IP with the port

use ifconfig on MAC to get the local private IP

it will be something like this

en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500 options=6463<RXCSUM,TXCSUM,TSO4,TSO6,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM> ether c8:89:f3:e5:a0:76 inet6 fe80::146a:e366:7e9c:5d5en0 prefixlen 64 secured scopeid 0xf inet 192.168.3.50 netmask 0xffffff00 broadcast 192.168.3.255 nd6 options=201<PERFORMNUD,DAD> media: autoselect status: active

in my case is 192.168.3.50 and i was running a backend with graphql in the port 4000

so the URL in the app will be http://192.168.3.50:4000/graphql

and that's it no permissions required, i only tested on IOS so I don't know if the same solutions works for Android

import {GraphQLClient} from 'graphql-request';
import {Platform} from 'react-native';

export const client = new GraphQLClient(
  Platform.OS === 'ios'
    ? 'http://192.168.3.50:4000/graphql' // I have before localhost:4000/graphql
    : 'http://10.0.2.2:4000/graphql',
  {
    credentials: 'include',
    mode: 'cors',
  },
);
Siumauricio
  • 101
  • 1
  • 5