react-native: iOS: NetInfo.isConnected returns always false

I am currently running RN 0.28.0…

NetInfo.isConnected.fetch().then(isConnected => {
      // variable isConnected is always false
    });

I would do a PR but I can’t code for iOS.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 49
  • Comments: 139 (25 by maintainers)

Commits related to this issue

Most upvoted comments

According to @robertodias180 suggestions I came up to wrapper for isConnected, It seems to work, but I wonder if it will work in all cases

export function isNetworkConnected() {
  if (Platform.OS === 'ios') {
    return new Promise(resolve => {
      const handleFirstConnectivityChangeIOS = isConnected => {
        NetInfo.isConnected.removeEventListener('change', handleFirstConnectivityChangeIOS);
        resolve(isConnected);
      };
      NetInfo.isConnected.addEventListener('change', handleFirstConnectivityChangeIOS);
    });
  }

  return NetInfo.isConnected.fetch();
}

Imho NetInfo.isConnected.fetch() is an important feature and should be fixed asap!

@javache @sahrens @davidaurelio @janicduplessis @hramos @shergin @rigdern is there any timeline for this?

Thx!

I solved it this way:

componentDidMount() {
  const dispatchConnected = isConnected => this.props.dispatch(setIsConnected(isConnected));

  NetInfo.isConnected.fetch().then().done(() => {
    NetInfo.isConnected.addEventListener('change', dispatchConnected);
  });
}

Can we stop with the +1 comments on this issue please, this is what reactions are used for.

When I try the addEventListener solution NetInfo only detects one or two connection changes. For example, if I start with a connection and then drop it Netinfo will detect that, but if I reconnect it will fail to detect that I’m now connected. NetInfo is producing an awful lot of false positives in my app, even with the workaround. Is anyone else experiencing the same?

I no longer trust NetInfo (react-native 0.53.3) This is what I do to make sure there’s internet:

async function handleConnectivityChange(status) {
  const { type } = status;
  let probablyHasInternet;
  try {
    const googleCall = await fetch(
        'https://google.com', {
        headers: {
          'Cache-Control': 'no-cache, no-store, must-revalidate',
          Pragma: 'no-cache',
          Expires: 0,
        },
      });
    probablyHasInternet = googleCall.status === 200;
  } catch (e) {
    probablyHasInternet = false;
  }

  console.log(`@@ isConnected: ${probablyHasInternet}`);

}

  NetInfo.getConnectionInfo().then(handleConnectivityChange);
  NetInfo.addEventListener('connectionChange', handleConnectivityChange);

If this is unlikely to get fixed soon, can we at least get the docs updated to indicate that this doesn’t work in iOS?

the same issue to me, I am using RN@0.30.0 and the fetch always return unknown.

But we can use addEventListener instead.

NetInfo.addEventListener('change',
    (networkType)=> {
        this.setState({networkType})
    }
)

instead of:

NetInfo.fetch().done(
    (networkType)=> {
        this.setState({networkType})
    }
)

Adding an initial connection event listener will cause NetInfo.getConnectionInfo() and NetInfo.isConnected.fetch() to work properly on the first call.

const onInitialNetConnection = isConnected => {
    console.log(`Is initially connected: ${isConnected}`);

    NetInfo.isConnected.removeEventListener(
        onInitialNetConnection
    );
};

NetInfo.isConnected.addEventListener(
    'connectionChange',
    onInitialNetConnection
);

// both now work on the first call.
await NetInfo.getConnectionInfo();
await NetInfo.isConnected.fetch();

Thanks #8469

Make sure to import this file or else it will not execute.

P.S. If you are using fetch, use method: 'HEAD' to reduce the amount of data transmitted.

Tried something similar, I keep running into the situation that the eventListener doesn’t detect it when the connection returns (on the simulator).

Reload app without internet connection => Detected Turn internet back on => NOT detected

Reload app with internet connection => Detected Turn internet off => Detected Turn internet back on => NOT detected

@autobind
export default class ConnectionToast extends React.Component {

    constructor(props){
        super(props);
    }

    componentDidMount() {
        NetInfo.isConnected.fetch()
            .then(this._handleConnectionInfoChange)
            .done(this._addListener);
     }

     componentWillUnmount(){
         this._removeListener();
     }

     _addListener(){
         NetInfo.isConnected.addEventListener(
             'change',
             this._handleConnectionInfoChange
         );
     }

     _removeListener(){
         NetInfo.isConnected.removeEventListener(
            'change',
            this._handleConnectionInfoChange
        );
     }

    _handleConnectionInfoChange(isConnected){
        this.props.connectActions.netInfo(isConnected);

        dbg('connected', isConnected);

        // this._removeListener();
        // this._addListener();

    }

[...]

Tried removing and re-adding the listener with every change, but no joy.

I’ve reported the same issue before: https://github.com/facebook/react-native/issues/8469

Quick workaround is to add an event handler NetInfo.isConnected.addEventListener('change', Function.prototype)

just a small update on the solutions by @Knight704 and @Ignigena as NetInfo.fetch is deprecated now. This did the trick for me:

const isNetworkConnected = () => {
  return NetInfo.getConnectionInfo().then(reachability => {
    if (reachability.type === 'unknown') {
      return new Promise(resolve => {
        const handleFirstConnectivityChangeIOS = isConnected => {
          NetInfo.isConnected.removeEventListener('connectionChange', handleFirstConnectivityChangeIOS);
          resolve(isConnected);
        };
        NetInfo.isConnected.addEventListener('connectionChange', handleFirstConnectivityChangeIOS);
      });
    }
    return (reachability.type !== 'none' && reachability.type !== 'unknown')
  });
}

@florentsorel so in my project I use redux for state management and setIsConnected is my action creator, which looks something like:

actions/network.js

export const setIsConnected = (isConnected) => ({
  type: 'SET_IS_CONNECTED',
  isConnected
});

and then I have a network reducer:

reducers/network.js

const initialState = {
  isConnected: true
};

const network = (state = initialState, action) => {
  switch (action.type) {
    case 'SET_IS_CONNECTED':
      return {
        isConnected: action.isConnected
      };

    default:
      return state;
  }
}

export default network;

This issue bugged me for a while and I indirectly ended up creating a small utility library focused on RN offline/online detection, encompassing typical React patterns and a nice integration with redux.

It works properly on real devices, both Android and iOS. However, I can also corroborate that sometimes the listener is not triggered in the iOS simulator and the connectivity state gets inconsistent. I haven’t yet tried the solutions proposed by @knight704 and @Ignigena (here and here respectively) but will probably test them out soon when I get some time.

A PR is also very welcome in case of more folks confirming that those solutions work like a charm!

Make sure you have

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

set in your android manifest file. This one tripped me up.

Its showing offline in IOS even though its connected RN – 0.50

NetInfo.isConnected.fetch().then(isConnected => { console.log('First, is ' + (isConnected ? 'online' : 'offline')); });

@kodatech This module should help you resolve that issue. I have used it recently on a project and highly recommend it https://github.com/rauliyohmc/react-native-offline

I had to build a weird workaround in order to solve this. For some reason one method works on android and the other on iOS. A fix to this would be great.

function handleFirstConnectivityChange(isConnected) {
	if (!sConnected) {
	 // do action
  	} 
  	NetInfo.isConnected.removeEventListener('change', handleFirstConnectivityChange);
}

if (Platform.OS === 'ios') {
  	NetInfo.isConnected.addEventListener('change', handleFirstConnectivityChange); 
} else {
  	NetInfo.isConnected.fetch().then(isConnected => {
  	if (!sConnected) {
    	// do action
 	} 
}

@afilp i just follow above example for ios

    if (Platform.OS === "ios") {
            isConnected = await fetch("https://www.google.com");
            if (isConnected) { }
  } else { 
    NetInfo.isConnected.fetch().done(
                (isConnected) => {
                    if (isConnected) { }
} 

I still need around many testing before consider as stable. now using back 0.530

let isConnected = false;

Platform.OS === "ios" 
  ? (isConnected = await fetch("https://www.google.com"))
  : (isConnected = await NetInfo.isConnected.fetch());

Platform.OS === "ios" && (isConnected.status = 200)
  ? (isConnected = true)
  : (isConnected = false); 

console.log(isConnected); //Result

I am using the Netinfo listener events as follows, it works great on both platforms. “react-native”: “0.43.4”,

componentDidMount(){
        NetInfo.isConnected.addEventListener('change', this._handleConnectivityChange);
 
    }

    componentWillUnmount(){
        NetInfo.isConnected.removeEventListener('change', this._handleConnectivityChange);
    }

    _handleConnectivityChange = (isConnected) => {
         console.warn(isConnected);
    }

I am having the same issue on v0.36.0

On 0.55.4 this is still a bug, and I just got hit on production 😢

@octopitus , @martinentelect this is my code, I am using react native version 0.55.4, and its a sensible and working workaround until NetInfo is fixed. In my api.js file I have this code

import { NetInfo } from 'react-native';
import { url } from '[AppName]/src/config';

export const onInitialNetConnection = () => {
    NetInfo.isConnected.removeEventListener(onInitialNetConnection);
};

const getDataById = async (dataId) => {
    // both now work on the first call.
    await NetInfo.getConnectionInfo();
    const isConnected = await NetInfo.isConnected.fetch();
    if (!isConnected) {
        throw new Error('networkError');
    }
    const response = await fetch(`${url}${dataId}`, {
        headers: {
            'Cache-Control': 'no-cache, no-store, must-revalidate',
            Pragma: 'no-cache',
            Expires: 0,
        },
    });
    if (response.ok) {
        return response.json();
    }
    throw new Error('invalidCode');
};

export default getDataById;

In my App.js file (my entry point to the application) I add the event listener:

NetInfo.isConnected.addEventListener(
            'connectionChange',
            onInitialNetConnection,
        );

@kddc thanks for the workaround. I am encountering an issue with it in my iOS device and scenario is as follows:

  1. ios -> airplane mode is on
  2. perform fetch -> getting ‘network error’, isConnected is false and reachability.type is none
  3. ios -> airplane mode is off
  4. perform fetch -> getting ‘network error’, isConnected is false and reachability.type is none

The entire time the application is running, in the background while changing airplane mode. Only restarting the app “catch” the new network connection.

ios: first request. if server crash,phone has wifi. NetInfo.isConnected.fetch().then((isConnected) => {alway false} 0.49.3.

I am also Having issue in react 0.47.0 with ios .

And there are many features and functionality not used by Facebook so it’s hard for us to prioritize them and prefer community PRs where the author can verify the fix for their bug/usecase.

The underlying code hasn’t really changed so I would be surprised if this is “fixed”.

I put fix in quotes because it seems like the motivation for this change is to only set up the native machinery that watches the network status if the application code is interested in observing it.

Apps register interest by adding an event listener which then starts checking reachability.

At this point it seems like this is the desired behaviour so perhaps a documentation change is required. NetInfo.isConnected returns a boolean so it doesn’t express that the state is unknown and that that won’t change until you add an event listener. Alternatively the function could be changed to return null or a boolean.

My apps have code that is very similar to what @knowbody posted. I don’t call NetInfo.isConnected.fetch() at all, however, as it’s useless at startup.

i assume there is connectivity, add the listener, and then the state will be updated when the connectivity state is no longer unknown.

Upgraded to 55 specifically because the changelog said it’s supposed to fix this – still broken (0.55.3).

I am able to get it to work using a similar solution to @yuyao110120:

NetInfo.isConnected.fetch().then(() => {
  NetInfo.isConnected.fetch().then(isConnected =>
    // isConnected is now the correct value
  });
});

Please fix.

At first I observed that NetInfo only wants to return the network status change after one cycle ( on -> off ). It turned out that it only has that problem in the simulator. On a real device it fires the callback properly even for multiple connect / disconnect cycles.

Sample code

  NetInfo.addEventListener( 'change', this._onConnectivityChange );

  _onConnectivityChange(connType) {
    // Do something with connType...
  }

same to 0.41.2

I am having this issue on 0.31, will this be fixed?

@SudoPlz I noticed that fetch caches the request on Android. You’d have to give it some headers to prevent that.

      const googleRequest = await fetch('https://www.google.com', {
        headers: {
          'Cache-Control': 'no-cache, no-store, must-revalidate',
          'Pragma': 'no-cache',
          'Expires': 0
        }
      });

RN 0.54.2 OS: iOS same issue, always unknown

When I Upgrade RN to 0.55.3

I’m use this code, first return unknown, And then it works

    NetInfo.getConnectionInfo()
        .then()
        .done(() => {
                switch (connectionInfo.type) {
                    case 'none':
                        Alert.alert('aa', 'none');
                        break;
                    case 'wifi':
                        Alert.alert('aa', 'wifi');
                        break;
                    case 'cellular':
                        if (connectionInfo.effectiveType !== 'unknown') {
                            Alert.alert('aa', `cellular ${connectionInfo.effectiveType}`);
                        } else {
                            Alert.alert('aa', 'cellular unknown');
                        }
                        break;
                    case 'unknown':
                        Alert.alert('aa', 'unknown');
                        break;
                    default:
                        Alert.alert('aa', 'default');
                        break;
                }
            });
        });

Ok, so let’s do a little trick.

    NetInfo.getConnectionInfo()
        .then()
        .done(() => {
            NetInfo.getConnectionInfo().then(connectionInfo => {
                switch (connectionInfo.type) {
                    case 'none':
                        Alert.alert('aa', 'none');
                        break;
                    case 'wifi':
                        Alert.alert('aa', 'wifi');
                        break;
                    case 'cellular':
                        if (connectionInfo.effectiveType !== 'unknown') {
                            Alert.alert('aa', `cellular ${connectionInfo.effectiveType}`);
                        } else {
                            Alert.alert('aa', 'cellular unknown');
                        }
                        break;
                    case 'unknown':
                        Alert.alert('aa', 'unknown');
                        break;
                    default:
                        Alert.alert('aa', 'default');
                        break;
                }
            });
        });

This my solution. Expect an official solution.

When your pull request gets merged, Facebook first incorporates it into their internal React Native repo and runs additional tests on it among other things. When that is successful, the change gets exported back to the GitHub repo.

By looking at your pull request’s activity, you can see your commit is now merged on GitHub. By looking at the commit info, you can see which branches it’s included in. Currently, it’s only in the “master” branch:

image

For comparison, if you look at an older commit you can see it appears in a number of branches:

image

At the beginning of every month, a new release candidate branch is created based on “master”. So your change will likely be in the next release candidate at the beginning of March. You can read more about React Native’s release schedule in this blog post.

Adam Comella Microsoft Corp.

@alsh76 for me its working when the app is in background, it doesn’t work when the app is killed from the multitasking menu. I resolved that by calling the following function whenever my home screen is launched. if(Platform.OS === 'android'){ NetInfo.isConnected.fetch().then(isConnected => { if(!isConnected) return; //Do something }); }

The line to add to you AndroidManifest.xml is:

 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

I’m having this issue on v0.34.1

Edit: Updated to v0.35.0 and same problem.

we have found this bug in 0.29.2…

I am using iPhone 8 - 11.3 simulator and on RN version 0.55.4 NetInfo.isConnected.fetch().then(isConnected => console.log(‘isConnected’, isConnected)) Always returns false. This is a problem in production on phone as well. I tried variations of mobile data and wifi.

When switching back to RN version 0.55.3 it seems to be working again.

Also having the same issue as users above. NetInfo event listeners seem to be very unreliable. I have tried many of the above workarounds without stable results. For now I’ll rely on fetching https://google.com or a health endpoint on my own servers to ensure my users have an internet connection.

0.53 always false … @deepaksasken … seem i need to disable the code only for android … Cannot use 0.54 to many bugs.

Thanks for the fix @alburdette619 So, this is fixed in will be available in version 0.54?

The problem appears to be only with the fetch api… however, adding event listener is working on iOS… However, addEventListener doesn’t work on android, so you need to use fetch api 😃

Here’s my snippet to make it work on both worlds:

function handleFirstConnectivityChange(isConnected) {
  console.log('Then, is ' + (isConnected ? 'online' : 'offline'))
  NetInfo.isConnected.removeEventListener(
    'connectionChange',
    handleFirstConnectivityChange,
  )
}

if (Platform.OS === 'android') {
    NetInfo.isConnected.fetch().then(isConnected => {
      console.log('First, is ' + (isConnected ? 'online' : 'offline'))
    })
  } else {
    NetInfo.isConnected.addEventListener(
      'connectionChange',
      handleFirstConnectivityChange,
    )
  }

@JamesDorrian thanks, I ended up doing something similar, pinging “google.com”.

Here’s the snippet:

// /utilities/connectivity.js

import { Alert } from 'react-native';

export function warning () {
  Alert.alert(
    'No connection',
    'Mmm... you seem to be offline'
  );
};

export async function checkAsync () {
  try {
    const res = await fetch('https://google.com');
    if (res.status === 200) return true;
  } catch (e) {
    warning();
  }
  return false;
};

Still, this is just a temporary workaround, and I think that NetInfo.isConnected should be fixed asap!

@pensierinmusica this issue should most definitely be prioritised. How difficult can is be to have a boolean return from the .isConnected() ?

I’m facing a strange behaviour because isConnected return true when connect to wifi (so far is ok) but if the modem don’t reach internet, isConnected is still true. I’m not really sure how to check effectively the internet connection, not just the network connection. Any ideas? Thanks

Experiencing the same issue as @SteffeyDev when I use the fix from @Knight704 in https://github.com/facebook/react-native/issues/8615#issuecomment-287977178 In most cases, this works just fine, however when I have pushed or popped modals the same code will never resolve as I am assuming the change event was already emitted.

I noticed that under these conditions, NetInfo.fetch() would return an unknown state (this will trigger a false under NetInfo.isConnected.fetch() and so I was able to work around it with the following modification to the original workaround:

export function isNetworkConnected() {
  return NetInfo.fetch().then(reachability => {
    if (reachability === 'unknown') {
      return new Promise(resolve => {
        const handleFirstConnectivityChangeIOS = isConnected => {
          NetInfo.isConnected.removeEventListener('change', handleFirstConnectivityChangeIOS);
          resolve(isConnected);
        };
        NetInfo.isConnected.addEventListener('change', handleFirstConnectivityChangeIOS);
      });
    }
    reachability = reachability.toLowerCase();
    return (reachability !== 'none' && reachability !== 'unknown');
  });
}

The unknown condition is only triggered on iOS since Android returns UNKNOWN. This is also the reason for lowercasing before returning the status since this should more or less retain the existing behavior from NetInfo.isConnected.fetch.

RN 0.38.1 and earlier, iOS Simulator.

componentDidMount() {
    NetInfo.isConnected.fetch().then(this._handleConnectionInfoChange);
    NetInfo.isConnected.addEventListener(
        'change',
        this._handleConnectionInfoChange
    );
 }

 componentWillUnmount(){
     NetInfo.isConnected.removeEventListener(
        'change',
        this._handleConnectionInfoChange
    );
 }

_handleConnectionInfoChange(isConnected){
    this.props.connectActions.netInfo(isConnected);

    dbg('connected', isConnected);

};

The initial NetInfo.isConnected.fetch().then(this._handleConnectionInfoChange); correctly retrieves the current connection status. However, subsequent changes to the connection status are not captured by the eventListener.

I have been having this issue for many RN version now.

@knowbody snippet works for me in 0.36

same on v0.35.0