react-native-netinfo: isInternetReachable is returning null on first attempt on ios

Environment

System: OS: macOS 12.1 CPU: (8) x64 Intel® Core™ i5-1038NG7 CPU @ 2.00GHz Memory: 1.02 GB / 16.00 GB Shell: 5.8 - /bin/zsh Binaries: Node: 17.0.1 - /usr/local/bin/node Yarn: 1.22.17 - /usr/local/bin/yarn npm: 8.1.0 - /usr/local/bin/npm Watchman: 2021.11.08.00 - /usr/local/bin/watchman Managers: CocoaPods: 1.11.2 - /usr/local/bin/pod SDKs: iOS SDK: Platforms: DriverKit 21.2, iOS 15.2, macOS 12.1, tvOS 15.2, watchOS 8.3 Android SDK: Not Found IDEs: Android Studio: 4.0 AI-193.6911.18.40.6626763 Xcode: 13.2.1/13C100 - /usr/bin/xcodebuild Languages: Java: Not Found npmPackages: @react-native-community/cli: Not Found react: 17.0.2 => 17.0.2 react-native: 0.66.3 => 0.66.3 react-native-macos: Not Found npmGlobalPackages: react-native: Not Found

Platforms

iOS

Versions

  • iOS: 15.2.1
  • react-native-netinfo: “^7.0.0”
  • react-native: “0.66.3”
  • react: “17.0.2”

Description

This is happening on real device (iPhone 11) On my app, in an on click event, i am trying to get some data from api. Before that i am checking internet connectivity. When i’m using this

import NetInfo from "@react-native-community/netinfo";

NetInfo.fetch().then(internetState => {
  console.log(internetState);
  if (internetState.isConnected && internetState.isInternetReachable) {
    // rest of my code
  }else{
    // some warning
  }
});

on first attempt it is returning this, with isInternetReachable is returning null. LOG {“details”: {“bssid”: null, “ipAddress”: “192.168.1.2”, “isConnectionExpensive”: false, “ssid”: null, “subnet”: “255.255.255.0”}, “isConnected”: true, “isInternetReachable”: null, “type”: “wifi”}

but if i click the button again, now isInternetReachable is returning true LOG {“details”: {“bssid”: null, “ipAddress”: “192.168.1.2”, “isConnectionExpensive”: false, “ssid”: null, “subnet”: “255.255.255.0”}, “isConnected”: true, “isInternetReachable”: true, “type”: “wifi”}

Its only happening on ios. any idea guys?

Reproducible Demo

https://snack.expo.dev/@rassemdev/login-page

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 8
  • Comments: 25 (7 by maintainers)

Most upvoted comments

“their” --> It is open source. There is no “they”, you are the they! All users of the library are the library developers! Welcome to the club 🤗 "waiting … " --> no need to wait! Make a PR with the fix!

This is what I am using right now:

import NetInfo from '@react-native-community/netinfo';
import { useEffect } from 'react';

export const useInternet = () => {

  useEffect(() => {
    // Fixes first call to NetInfo.fetch() returning `isInternetReachable` null on iOS
    // https://github.com/react-native-netinfo/react-native-netinfo/issues/572
    NetInfo.fetch();
  }, []);

  const hasConnectionError = async () => {
    const state = await NetInfo.fetch();
    const { isConnected, isInternetReachable } = state;
    return !isConnected || !isInternetReachable;
  };

  return { hasConnectionError };
};

@rassemdev I’ve just confirmed your issue on iOS. The first call to the listener set with NetInfo.addEventListener returns isInternetReachable = null. However, a second callback is fired right after (~100ms later) with the correct value.

I don’t know the exact cause, but my guess above still applies (the time between the check is made and the header request response is received). You should not rely on one single result when checking whether there’s internet or not, as it will be constantly changing.

I will still dig a bit to see if something can be improved, but I doubt it.

so do we know for sure if this is only on the first call? If you send out two requests close enough together, will the second one always return with a value? I think the recursive call is likely the best answer in this thread, but the real solution is to have some indicator as to why that value is null in the first place, and not return the call from fetch until whatever needs to init has inited, right?

This is what I am using right now:

import NetInfo from '@react-native-community/netinfo';
import { useEffect } from 'react';

export const useInternet = () => {

  useEffect(() => {
    // Fixes first call to NetInfo.fetch() returning `isInternetReachable` null on iOS
    // https://github.com/react-native-netinfo/react-native-netinfo/issues/572
    NetInfo.fetch();
  }, []);

  const hasConnectionError = async () => {
    const state = await NetInfo.fetch();
    const { isConnected, isInternetReachable } = state;
    return !isConnected || !isInternetReachable;
  };

  return { hasConnectionError };
};

thank you for the idea, but i prefer this

import { useEffect, useRef } from 'react';
import NetInfo from '@react-native-community/netinfo';

const useInternet = () => {
  useEffect(() => {
    // Fixes first call to NetInfo.fetch() returning `isInternetReachable` null on iOS
    // https://github.com/react-native-netinfo/react-native-netinfo/issues/572
    NetInfo.fetch();
  }, []);

  const hasInternet = async () => {
    const { isConnected, isInternetReachable } = await NetInfo.fetch();
    return isConnected && isInternetReachable ? true : false;
  };

  return { hasInternet };
};

export default useInternet;

Definitely going to merge the refresh PR it’s just a holiday here, been busy

If there really is a platform issue I’d welcome any PR that fixes it

  isInternetReachable = (await NetInfo.fetch()).isInternetReachable;

  if (isInternetReachable) {
    return isInternetReachable;
  }

  await timeout(200);

  isInternetReachable = (await NetInfo.fetch()).isInternetReachable;

No, I’m still waiting for their fix.