Skip to content

[Bug]: The Navigation View is not working correctly in IOS #435

@FardeenNiyaziHM

Description

@FardeenNiyaziHM

Is there an existing issue for this?

  • I have searched the existing issues

Description of the bug

the navigation view works fine on Andorid
But in Ios it's not working correctly sometimes it starts navigation, but most of the time it's not working correctly I just getting the black map and also it's not detecting the device location internally

mapViewController.getMyLocation() return undefined even though I have given permission

React Native version

0.74.1

React version

18.2.0

Package version

0.9.3

Native SDK versions

  • I haven't changed the version of the native SDKs

React Native Doctor Output

WARNING: You should run npx react-native@latest to ensure you're always using the most current version of the CLI. NPX has cached version (0.74.1) != current release (0.79.2)

warn Package rn-fetch-blob contains invalid configuration: "dependency.hooks" is not allowed. Please verify it's properly linked using "npx react-native config" command and contact the package maintainers about this.
⠙ Running diagnostics...warn Package rn-fetch-blob contains invalid configuration: "dependency.hooks" is not allowed. Please verify it's properly linked using "npx react-native config" command and contact the package maintainers about this.
Common
✓ Node.js - Required to execute JavaScript code
✓ yarn - Required to install NPM dependencies
✓ Watchman - Used for watching changes in the filesystem when in development mode
● Metro - Metro Bundler is not running

Android
✓ Adb - Required to verify if the android device is attached correctly
✓ JDK - Required to compile Java code
✓ Android Studio - Required for building and installing your app on Android
✓ ANDROID_HOME - Environment variable that points to your Android SDK installation
✓ Gradlew - Build tool required for Android builds
✖ Android SDK - Required for building and installing your app on Android

  • Versions found: N/A
  • Version supported: 34.0.0

iOS
✓ Xcode - Required for building and installing your app on iOS
✓ Ruby - Required for installing iOS dependencies
✓ CocoaPods - Required for installing iOS dependencies
● ios-deploy - Required for installing your app on a physical device with the CLI
✓ .xcode.env - File to customize Xcode environment

Errors: 1
Warnings: 2

Steps to reproduce

Image

Expected vs Actual Behavior

The navigation should start in IOS, but it's not working

Code Sample

import React, {useState, useEffect, useCallback, useMemo} from 'react';
import {
  Alert,
  BackHandler,
  Platform,
  StatusBar,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';
import Snackbar from 'react-native-snackbar';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';

import {calculateDistanceTraveled} from '../../utils/misc';
import {strings} from '../../utils/strings';
import {
  NavigationView,
  useNavigation,
  RouteStatus,
  type ArrivalEvent,
  type Location,
  type NavigationViewController,
  type MapViewController,
  MapViewCallbacks,
  NavigationViewCallbacks,
  TravelMode,
  NavigationCallbacks,
  MapType,
} from '@googlemaps/react-native-navigation-sdk';
import usePermissions from './helpers/checkPermission';
import DirectionPanel from './DirectionPanel';
import MapBottomSheet from './MapBottomSheet';
import {useFocusEffect} from '@react-navigation/native';
import {stat} from 'react-native-fs';

// Utility function for showing Snackbar
const showSnackbar = (text: string, duration = Snackbar.LENGTH_SHORT) => {
  Snackbar.show({text, duration});
};

export const GoogleNavigationView = ({
  navigation,
  setDirectionRouteDetails,
  origin,
  currentLocation,
  setScreenData,
  imageAnnotations,
  handleSubmit,
  setTraveledPath,
  setArrived,
  arrived,
  handleErrorRefresh,
}: any) => {
  const {arePermissionsApproved} = usePermissions();
  const {navigationController, addListeners, removeListeners} = useNavigation();
  const [navigationViewController, setNavigationViewController] =
    useState<NavigationViewController | null>(null);
  const [mapViewController, setMapViewController] =
    useState<MapViewController | null>(null);
  const [navigationInitialized, setNavigationInitialized] = useState(false);
  const [showMapControls, setShowMapControls] = useState(false);
  const [isTrafficEnabled, setIsTrafficEnabled] = useState(true);
  const [directionSteps, setDirectionSteps] = useState([]);
  const [isSatelliteMode, setIsSatelliteMode] = useState(false);
  const [showDirections, setShowDirections] = useState(false);


  const getDirections = async () => {
    const startOrigin = `${origin[1]},${origin[0]}`;
    const destination = `${currentLocation[1]},${currentLocation[0]}`;

    const response = await fetch(
      `https://maps.googleapis.com/maps/api/directions/json?origin=${startOrigin}&destination=${destination}&key=${key}`,
    );
    const data = await response.json();
    console.log('Directions data:', data);
    console.log('data.routes[0].legs[0].steps', data.routes[0].legs[0].steps);
    const steps = data.routes[0]?.legs[0]?.steps || [];
    setDirectionSteps(steps);
  };

  const onRecenterButtonClick = useCallback(() => {
    console.log('onRecenterButtonClick');
  }, []);

  const navigationViewCallbacks: NavigationViewCallbacks = {
    onRecenterButtonClick,
  };

  console.log('oringin ', origin);

  console.log('currentLocation ', currentLocation);

  // 🔴 Android Back Button Handling

  useEffect(() => {
    const backAction = () => {
      Alert.alert('Hold on!', 'Are you sure you want to go back?', [
        {text: 'Cancel', onPress: () => null, style: 'cancel'},
        {
          text: 'YES',
          onPress: () => {
            navigationController.stopGuidance();
            navigationController.clearDestinations();
            navigationController.cleanup();
            navigation.replace(strings.MAP);
          },
        },
      ]);
      return true;
    };
    const backHandler = BackHandler.addEventListener(
      'hardwareBackPress',

      backAction,
    );

    return () => {
      // On screen unmount or back navigation
      if (navigationController) {
        navigationController.stopGuidance();
        navigationController.clearDestinations();
        navigationController.cleanup();
      }
      backHandler.remove();
    };
  }, []);

  // 🏁 Handle Arrival
  const onArrival = useCallback(
    (event: ArrivalEvent) => {
      if (!arrived) {
        setArrived(true);
        Snackbar.show({
          text: 'Arrived at Destination',
          duration: Snackbar.LENGTH_SHORT,
        });
        navigationController.stopGuidance();
        navigationController.clearDestinations();
        navigationController.cleanup();

        setTimeout(() => handleSubmit(), 1000);
      }
    },

    [arrived, handleSubmit, setArrived],
  );

  // 🚦 Route Progress Updates
  const onRemainingTimeOrDistanceChanged = useCallback(async () => {
    const progress = await navigationController.getCurrentTimeAndDistance();
    const LatLong = await navigationController.getTraveledPath();
    setTraveledPath(LatLong);
    console.log('LatLong : ', LatLong);
    console.log('progress : ', progress);

    const distanceInMeters = calculateDistanceTraveled(LatLong);
    console.log('Distance in Meters: ', distanceInMeters);
    console.log(
      `🚗 Distance Traveled: ${(distanceInMeters / 1000).toFixed(2)} km`,
    );
    setDirectionRouteDetails({
      distanceRemaining: progress.meters,
      distanceTraveled: distanceInMeters,
      durationRemaining: progress.seconds,
    });
  }, [navigationController]);

  // ❌ Handle Navigation Errors
  const onNavigationInitError = (errorCode: any) => {
    Alert.alert('Navigation Init Error', `Error Code: ${errorCode}`);
  };

  const onRouteStatusResult = (status: RouteStatus) => {
    if (status !== RouteStatus.OK) {
      Alert.alert('Route Error', `Status: ${status}`);
    }
  };

  const onNavigationReady = useCallback(async () => {
    console.log('onNavigationReady');
    if (mapViewController) {
      const loc = await mapViewController.getMyLocation();
      console.log('Fardeen - My location:', loc);
    }
    setNavigationInitialized(true);
  }, [mapViewController]);

  // 🎯 Register Callbacks
  const navigationCallbacks: NavigationCallbacks = useMemo(
    () => ({
      onArrival,
      onRemainingTimeOrDistanceChanged,
      onNavigationReady,
      onNavigationInitError,
      onRouteStatusResult,
    }),

    [onArrival, onRemainingTimeOrDistanceChanged],
  );

  useFocusEffect(
    useCallback(() => {
      addListeners(navigationCallbacks);

      return () => {
        removeListeners(navigationCallbacks);
      };
    }, [navigationCallbacks, addListeners, removeListeners]),
  );

  const onMapReady = useCallback(async () => {
    console.log('Map is ready, initializing navigator...');
    try {
      mapViewController?.setMyLocationButtonEnabled(true);
      mapViewController?.setMyLocationEnabled(true);
      mapViewController?.setZoomControlsEnabled(true);
      const isLocation = await mapViewController?.isMyLocationEnabled();
      console.log('isLocation : ', isLocation);

      await navigationController.init();
      getDirections();
    } catch (error) {
      console.error('Error initializing navigator', error);
      showSnackbar('Error initializing navigator');
    }
  }, [navigationController]);

  const mapViewCallbacks: MapViewCallbacks = useMemo(() => {
    return {
      onMapReady,
    };
  }, [mapViewController, onMapReady]);

  const startNavigation = useCallback(async () => {
    try {
      if (!origin || !currentLocation) {
        console.log('Origin or destination not set yet.');
        return;
      }
      const waypoint2 = {
        title: 'From',
        position: {
          lat: origin[1], // Latitude
          lng: origin[0], // Longitude
        },
      };
      const waypoint = {
        title: 'Destination',
        position: {
          lat: currentLocation[1], // Latitude
          lng: currentLocation[0], // Longitude
        },
      };

      const routingOptions = {
        travelMode: TravelMode.TWO_WHEELER, //  TravelMode.TWO_WHEELER
        avoidFerries: false,
        avoidTolls: false,
      };

      const displayOptions = {
        showDestinationMarkers: true,
        showStopSigns: true,
        showTrafficLights: true,
      };
      await navigationController.setDestinations(
        [waypoint],
        routingOptions,
        displayOptions,
      );
      await navigationController.startGuidance();
      console.log('Navigation started successfully!');
    } catch (error) {
      console.error('Error starting navigation', error);
    }
  }, [navigationController, origin, currentLocation]);

  // 🚀 Start navigation when initialized
  useEffect(() => {
    if (navigationInitialized) {
      startNavigation();
    }
  }, [navigationInitialized, startNavigation]);

  const setMapType = (isSatellite: boolean) => {
    if (!isSatellite) {
      console.log('Setting map type to NORMAL');
      mapViewController?.setMapType(MapType.NORMAL);
    } else {
      console.log('Setting map type to SATELLITE');
      mapViewController?.setMapType(MapType.SATELLITE);
    }
  };

  console.log('isSatelliteMode', isSatelliteMode);

  return (
    <View style={styles.container}>
      <StatusBar barStyle={'light-content'} />
      <NavigationView
        style={{flex: 1}}
        androidStylingOptions={{
          primaryDayModeThemeColor: '#34eba8',
          headerDistanceValueTextColor: '#76b5c5',
          headerInstructionsFirstRowTextSize: '20f',
        }}
        iOSStylingOptions={{
          navigationHeaderPrimaryBackgroundColor: '#34eba8',
          navigationHeaderDistanceValueTextColor: '#76b5c5',
        }}
        navigationViewCallbacks={navigationViewCallbacks}
        mapViewCallbacks={mapViewCallbacks}
        onNavigationViewControllerCreated={setNavigationViewController}
        onMapViewControllerCreated={setMapViewController} // Added MapViewController
      />

      <TouchableOpacity
        style={styles.directionButton}
        onPress={() => setShowDirections(!showDirections)}>
        <MaterialIcons
          name={showDirections ? 'cancel' : 'directions'}
          size={30}
          color="#fff"
        />
      </TouchableOpacity>

      <TouchableOpacity
        style={styles.stopNavigation}
        onPress={() => {
          setScreenData(4);
          setShowDirections(false);
        }}>
        <MaterialIcons name={'cancel'} size={30} color="#fff" />
      </TouchableOpacity>

      {showDirections && (
        <MapBottomSheet
          steps={directionSteps}
          isSatellite={isSatelliteMode}
          onToggleSatellite={val => {
            setMapType(val);
            setIsSatelliteMode(val);
          }}
        />
      )}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  floatingButton: {
    position: 'absolute',
    bottom: 200,
    right: 20,
    backgroundColor: '#1E90FF',
    paddingVertical: 12,
    paddingHorizontal: 20,
    borderRadius: 25,
    elevation: 5,
    zIndex: 1000,
  },
  buttonText: {
    color: '#fff',
    fontWeight: 'bold',
  },
  directionButton: {
    backgroundColor: '#007AFF',
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 25,
    height: 40,
    width: 40,
    position: 'absolute',
    bottom: 200,
    right: 20,
  },
  stopNavigation: {
    backgroundColor: '#007AFF',
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 25,
    height: 40,
    width: 40,
    position: 'absolute',
    bottom: 260,
    right: 20,
  },
});

export default GoogleNavigationView;

Additional Context

Image

Many Time The Roue Navigation not started in Ios

Metadata

Metadata

Assignees

Labels

type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions