Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is distanceFilter working for AppleSettings? #1116

Open
ahaaje opened this issue Aug 4, 2022 · 15 comments
Open

Is distanceFilter working for AppleSettings? #1116

ahaaje opened this issue Aug 4, 2022 · 15 comments
Labels
P2 Important issues not at the top of the work list. platform: ios Issue is related to the iOS platform type: bug Something isn't working

Comments

@ahaaje
Copy link

ahaaje commented Aug 4, 2022

💬 Questions and Help

I have initialized my Geolocator stream for iOS like this

if (defaultTargetPlatform == TargetPlatform.iOS ||
        defaultTargetPlatform == TargetPlatform.macOS) {
      locationSettings = AppleSettings(
        accuracy: LocationAccuracy.high,
        activityType: ActivityType.automotiveNavigation,
        distanceFilter: 50,
        pauseLocationUpdatesAutomatically: true,
        // Only set to true if our app will be started up in the background.
        showBackgroundLocationIndicator: false,
      );
    }

final positionStream = _geolocatorPlatform.getPositionStream(
          locationSettings: locationSettings);

However, I seem to be receiving updates every second regardless of distance. This is an example of data received

[
  {
    "timestamp": "2022-08-04 13:10:09",
    "distance": 0
  },
  {
    "timestamp": "2022-08-04 13:10:10",
    "distance": 7.9757947805834
  },
  {
    "timestamp": "2022-08-04 13:10:11",
    "distance": 2.2674748423432
  },
  {
    "timestamp": "2022-08-04 13:10:12",
    "distance": 4.141630343381
  },
  {
    "timestamp": "2022-08-04 13:10:13",
    "distance": 1.5377582965926
  },
  {
    "timestamp": "2022-08-04 13:10:14",
    "distance": 1.1669075782772
  },
  {
    "timestamp": "2022-08-04 13:10:15",
    "distance": 0.27485665948729
  },
  {
    "timestamp": "2022-08-04 13:10:16",
    "distance": 0.66052748583651
  },
  {
    "timestamp": "2022-08-04 13:10:17",
    "distance": 0.34274642153663
  },
  {
    "timestamp": "2022-08-04 13:10:18",
    "distance": 0.40991233672361
  },
  {
    "timestamp": "2022-08-04 13:10:19",
    "distance": 0.5831300397487
  },
  {
    "timestamp": "2022-08-04 13:10:20",
    "distance": 0.00000003401181987862
  },
  {
    "timestamp": "2022-08-04 13:10:32",
    "distance": 1.3311573986273
  },
  {
    "timestamp": "2022-08-04 13:10:33",
    "distance": 1.2222122786832
  },
  {
    "timestamp": "2022-08-04 13:10:34",
    "distance": 0.28793988602762
  },
  {
    "timestamp": "2022-08-04 13:10:35",
    "distance": 0.4164630124995
  },
  {
    "timestamp": "2022-08-04 13:10:36",
    "distance": 0.30794720696947
  },
  {
    "timestamp": "2022-08-04 13:10:37",
    "distance": 3.4094678449345
  },
  {
    "timestamp": "2022-08-04 13:10:38",
    "distance": 7.9905255943094
  },
  {
    "timestamp": "2022-08-04 13:10:39",
    "distance": 6.5815206312327
  },
  {
    "timestamp": "2022-08-04 13:10:40",
    "distance": 11.338309130389
  },
  {
    "timestamp": "2022-08-04 13:10:41",
    "distance": 13.215053045416
  },
  {
    "timestamp": "2022-08-04 13:10:42",
    "distance": 15.486864727375
  },
  {
    "timestamp": "2022-08-04 13:10:43",
    "distance": 16.336293198668
  },
  {
    "timestamp": "2022-08-04 13:10:44",
    "distance": 17.58629081234
  },
  {
    "timestamp": "2022-08-04 13:10:45",
    "distance": 18.380570582603
  },
  {
    "timestamp": "2022-08-04 13:10:46",
    "distance": 19.141020237324
  },
  {
    "timestamp": "2022-08-04 13:10:47",
    "distance": 18.563242076721
  },
  {
    "timestamp": "2022-08-04 13:10:48",
    "distance": 18.324582857798
  },
  {
    "timestamp": "2022-08-04 13:10:49",
    "distance": 19.284692748053
  },
  {
    "timestamp": "2022-08-04 13:10:50",
    "distance": 18.381268944416
  },
  {
    "timestamp": "2022-08-04 13:10:51",
    "distance": 17.915914755432
  },
  {
    "timestamp": "2022-08-04 13:10:52",
    "distance": 18.221607618619
  },
  {
    "timestamp": "2022-08-04 13:10:55",
    "distance": 61.908366570742
  },
  {
    "timestamp": "2022-08-04 13:10:56",
    "distance": 18.260683415718
  },
  {
    "timestamp": "2022-08-04 13:10:58",
    "distance": 19.182044286679
  },
  {
    "timestamp": "2022-08-04 13:10:58",
    "distance": 16.727495452973
  },
  {
    "timestamp": "2022-08-04 13:11:22",
    "distance": 447.62542741244
  },
  {
    "timestamp": "2022-08-04 13:11:23",
    "distance": 19.13914836906
  },
  {
    "timestamp": "2022-08-04 13:11:24",
    "distance": 15.278570182625
  },
  {
    "timestamp": "2022-08-04 13:11:25",
    "distance": 21.834957704957
  },
  {
    "timestamp": "2022-08-04 13:11:26",
    "distance": 19.395564454341
  },
  {
    "timestamp": "2022-08-04 13:11:27",
    "distance": 19.93235735733
  },
  {
    "timestamp": "2022-08-04 13:11:28",
    "distance": 20.518736611075
  },
  {
    "timestamp": "2022-08-04 13:11:29",
    "distance": 20.728230757645
  },
  {
    "timestamp": "2022-08-04 13:11:30",
    "distance": 20.40131998597
  },
  {
    "timestamp": "2022-08-04 13:11:31",
    "distance": 19.927232099813
  },
  {
    "timestamp": "2022-08-04 13:11:32",
    "distance": 19.900167173362
  },
  {
    "timestamp": "2022-08-04 13:11:33",
    "distance": 17.142442188899
  },
  {
    "timestamp": "2022-08-04 13:11:34",
    "distance": 18.734180067823
  }
]

For a 64 km drive, this resulted in 3586 updates with an average distance of 17.7 meters. The huge number of updates use processing time and power, and storage. I set my distanceFilter to 50 meters to prevent that many updates, but it seems to be not working. Is there a way to do that?

@florissmit1
Copy link
Contributor

Hey @ahaaje, thanks for reporting your issue. I'm not able to reproduce your issue, and the code in regard to the distanceFilter seems fine to me.

The distanceFilter should work fine on iOS/macOS. I can imagine that the ActivityType.automotiveNavigation has an impact on the Position Stream, since the ActivityType is tracking location changes to the automobile. Can you try removing the parameter and see in what way the PositionStream behaves?

If the issue still occurs, can you help me by uploading your code in this thread, together with your flutter doctor -v?

@ahaaje
Copy link
Author

ahaaje commented Aug 7, 2022

It did not make any difference in the simulator when I removed the setting. I keep getting updated with 32-33 meters distance, even though the distanceFilter is set to 50m. I will provide more feedback when having seen the updated in action on physical devices.

@Sayene
Copy link

Sayene commented Oct 4, 2022

We can see the issue on iOS 16 simulator as well. Even though position is not changing at all the stream continues to fire callback every 1 second (with the same position value). pauseLocationUpdatesAutomatically is set to true; Changing activityType, accuracy and distanceFilter does not seem to have any effect.

@furkansoylemez
Copy link

Same here with @Sayene , any solutions ?

@zorro2b
Copy link

zorro2b commented Oct 28, 2022

I have tracked this to an interaction between the getCurrentPosition() and getPositionStream().

If you call getPositionStream() with your desired distanceFilter, it is set correctly. But as soon as you call getCurrentPosition() it changes the distanceFilter to kCLDistanceFilterNone.

Because there is only one CLLocationManager instance in use, this changes the filter for the position stream as well. So you then start receiving constant location updates.
Screen Shot 2022-10-28 at 4 31 45 pm

@TimHoogstrate TimHoogstrate added platform: ios Issue is related to the iOS platform and removed waiting for customer labels Jun 29, 2023
@JeroenWeener JeroenWeener added type: bug Something isn't working P2 Important issues not at the top of the work list. labels Jul 3, 2023
@skillastat
Copy link

Thanks for the information. I was calling the getCurrentPosition() in a method to send current location to server. Instead, I used a workaround to store the last known position inside the stream updates, and send this to the server when needed. That is the best alternative I found.

You only need to call the getCurrentPosition() once when initializing your geolocator instance.

Code:


Position? _lastKnownPosition;
  StreamSubscription<Position>? positionStream;

  Future<void> startLocationUpdates() async {        
    positionStream = Geolocator.getPositionStream(
      locationSettings: locationSettings,
    ).listen(
      (Position? position) {
        if (position != null) {          
          _currentPositionStreamController.add(position); 
          _lastKnownPosition = position;  // store the last known position
        } 
      },
    );
  }

@kevinZartek
Copy link

Facing similar issue

@OsamaQureshi796
Copy link

Is there any update on this issue cuz it's few months we are facing this issue.

@naveedrehman90001
Copy link

i am also facing same issue

@OsamaQureshi796
Copy link

Hi any update

@cs-hyunseo
Copy link

Same issue.
Even I don't call getCurrentPosition() .

@skillastat
Copy link

Same issue. Even I don't call getCurrentPosition() .

Refer to my response earlier. You have to get the last known position first.

You only need to call the getCurrentPosition() once when initializing your geolocator instance.

@saurabhkumar8112
Copy link

Thanks for the information. I was calling the getCurrentPosition() in a method to send current location to server. Instead, I used a workaround to store the last known position inside the stream updates, and send this to the server when needed. That is the best alternative I found.

You only need to call the getCurrentPosition() once when initializing your geolocator instance.

Code:


Position? _lastKnownPosition;
  StreamSubscription<Position>? positionStream;

  Future<void> startLocationUpdates() async {        
    positionStream = Geolocator.getPositionStream(
      locationSettings: locationSettings,
    ).listen(
      (Position? position) {
        if (position != null) {          
          _currentPositionStreamController.add(position); 
          _lastKnownPosition = position;  // store the last known position
        } 
      },
    );
  }

Thanks for this suggestion @skillastat
However one thing I don't i don't understand is, why do we need to call getCurrentPosition at least once as you said?
The location stream returns the correct current position and according to the settings described.
I got everything correctly even without calling getCurrentPosition once, i just used the stream

@skillastat
Copy link

Thanks for this suggestion @skillastat However one thing I don't i don't understand is, why do we need to call getCurrentPosition at least once as you said? The location stream returns the correct current position and according to the settings described. I got everything correctly even without calling getCurrentPosition once, i just used the stream

I don't know @saurabhkumar8112, I first used the documentation to setup my app. Taken from https://pub.dev/packages/geolocator

import 'package:geolocator/geolocator.dart';

Future _determinePosition() async {
bool serviceEnabled;
LocationPermission permission;

serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
return Future.error('Location services are disabled.');
}

permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
return Future.error('Location permissions are denied');
}
}

if (permission == LocationPermission.deniedForever) {
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}

return await Geolocator.getCurrentPosition();
}

Geolocator.getCurrentPosition() is checking for the real location of the device.

This being said, I had an issue in wich at some point in my app lifecycle, the stream was not updating anymore (After a couple hours). But this didn't happen everytime.... it was an elusive problem that I had to work around.

So now, what I do is check every 15 minutes if our Geolocator.getCurrentPosition() is > 100 meters from our last known position. If it is, we reset our stream. This seems to have fixed my problem since the last update I pushed to my users.

StreamSubscription<Position>? positionStream;
Position? _latestPosition;

VoidCallback? showLocationStreamErrorDialog;
late ValueNotifier<bool> syncResultNotifier;

  // syncResultNotifier is used to display a popup in the ui, wich informs the user that the position is not synced, with a loading indicator until we reset our position stream
void setSyncResultNotifier(ValueNotifier<bool> notifier) {
  syncResultNotifier = notifier;
}

Future<void> _startLocationUpdates() async {        
  positionStream = Geolocator.getPositionStream(
    locationSettings: locationSettings,
  ).listen(
    (Position? position) {
      if (position != null) {
        _latestPosition = position;
        _currentPositionStreamController.add(position);            
      } 
    },
    onError: (e) {                
      _restartLocationUpdates();
    },
  );    
  syncResultNotifier.value = true;
  actualPosition = null;    
  monitorStreamHealth();    
}

Timer? streamHealthTimer;
Position? actualPosition;

void monitorStreamHealth() async {
  streamHealthTimer?.cancel();
  streamHealthTimer = Timer.periodic(const Duration(minutes: 15), (Timer timer) async {
    
    if (_latestPosition == null) {        
      return;
    }

    try {
        Position actualPosition = await Geolocator.getCurrentPosition();

        // Calculate the distance difference between the two positions
        double distance = Geolocator.distanceBetween(
          _latestPosition!.latitude,
          _latestPosition!.longitude,
          actualPosition.latitude,
          actualPosition.longitude,
        );          

        if (distance > 100) {
            // Restart the location updates stream if _latestPosition distance is more than 100 meters compared to the actual device position
            _latestPosition = actualPosition;              
           _restartLocationUpdates();
        }                 
    } catch (e) { 
       // On error, restart the location updates stream  
      _restartLocationUpdates();
    }
  });
}  

Future<void> _restartLocationUpdates() async {
  if (syncResultNotifier.value == false) return; // Prevents multiple restarts in quick succession
  syncResultNotifier.value = false;    
  // show the stream unsync dialog
  showLocationStreamErrorDialog?.call();  
  // cancel the timer
  streamHealthTimer?.cancel();
  // cancel the stream
  await positionStream?.cancel();
  // Short delay before restart
  await Future.delayed(const Duration(seconds: 10));
  _startLocationUpdates();
}

@ksenia-lyagusha
Copy link
Contributor

I have tracked this to an interaction between the getCurrentPosition() and getPositionStream().

If you call getPositionStream() with your desired distanceFilter, it is set correctly. But as soon as you call getCurrentPosition() it changes the distanceFilter to kCLDistanceFilterNone.

Because there is only one CLLocationManager instance in use, this changes the filter for the position stream as well. So you then start receiving constant location updates. Screen Shot 2022-10-28 at 4 31 45 pm

Here's a PR to the library that fixes the issue in your comment #1600

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P2 Important issues not at the top of the work list. platform: ios Issue is related to the iOS platform type: bug Something isn't working
Projects
None yet
Development

No branches or pull requests