capacitor: Geolocation on iOS is slow because it is always using high accuracy

The geolocation plugin on iOS is taking a long time to get the users location because it is always using kCLLocationAccuracyBest or kCLLocationAccuracyBestForNavigation. Pull request #1773 added the option for enableHighAccuracy, but according to Apple’s developer documentation on kCLLocationAccuracyBestForNavigation: “Because of the extra power requirements, use this level of accuracy only while the device is plugged in.”. This level of accuracy seems like it is too high to be used for enableHighAccuracy.

The cordova plugin equivalent uses kCLLocationAccuracyThreeKilometers by default and kCLLocationAccuracyBest for high accuracy.

Ideally the user could choose the desired accuracy from all of the iOS options listed in the documentation, but these might not line up with android very well. Regardless the current implementation takes a long time to get the current location, especially when high accuracy is not required by the app.

Note that the long wait time to fetch the current location is not as noticeable on the simulator and a physical device should be used to see the true wait time.

About this issue

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

Most upvoted comments

I just found a workaround that returns the location in less than 50ms on my iOS device, even with enableHighAccuracy set to true:

const internalGetCurrentPosition = async (options: GeolocationOptions = {}): Promise<GeolocationPosition> => {
  return new Promise<GeolocationPosition>((resolve, reject) => {
    const id = Geolocation.watchPosition(options, (position, err) => {
      Geolocation.clearWatch({id});
      if(err) {
        reject(err);
        return;
      }
      resolve(position);
    });
  });
};

What tipped me of was this article that says that using startUpdatingLocation() and stopUpdatingLocation() can be much faster than using requestLocation()

I am having the same issue. In my case it didn’t matter if enableHighAccuracy was set to true or false, getting the current position on iOS took each time around 10 seconds. On android on the other hand the position is returned almost immediately.

It uses kCLLocationAccuracyBestForNavigation for enableHighAccuracy because some users complained about kCLLocationAccuracyBest results being bad.

We can change kCLLocationAccuracyBest for when enableHighAccuracy is false/not set for another value that has less accuracy and is faster.

Android also has multiple possible values, so maybe we can add a new property that can match some of the iOS values with similar Android values.

We are changing the whole android geolocation code on android for next major release, so hopefully will fix that

Any update on this? We’re also experiencing a 10 seconds delay in Geolocation.getCurrentLocation(), using the latest Capacitor 2.0.1 release and the enableHighAccuracy option enabled.

Any idea why @WIStudent’s workaround would work with the same high accuracy result?

Update: Ah, I’ve now read the link shared by @WIStudent, that explains:

requestLocation() might take longer to retrieve location data As mentioned in this WWDC video about CoreLocation (at around 11:55), requestLocation() is a convenient method provided by Apple which under the hood will run startUpdatingLocation() , retrieve multiple location data, and select the most accurate one to pass to delegate, and call stopUpdatingLocation(). This process can take up to 10 seconds (which is around the timeout limit) if it can’t decide which location data is the best.

Well, that explains it.

@leonelngande We switched back to the cordova plugin for now

Solution Works on iOS, but in android got blank screen.

i need to use

if(!this.platform.is('android')){
                const internalGetCurrentPosition = async (options: GeolocationOptions = {}): Promise<GeolocationPosition> => {
                          return new Promise<GeolocationPosition>((resolve, reject) => {
                            const id = Geolocation.watchPosition(options, (position, err) => {
                              Geolocation.clearWatch({id});
                              if(err) {
                                reject(err);
                                return;
                              }
                              resolve(position);
                            });
                          });
                        };

                internalGetCurrentPosition(options).then((resp) => {

}).catch((error) => {    

});
        }else{
             Geolocation.getCurrentPosition().then((resp) => {

}).catch((error) => {    

});
}

You need to request the permission first (you can use permissions plugin or just call Geolocation.requestPermissions();), then you can use it as in any browser.

https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API

But please, stop the off-topic, this issue is about iOS

const internalGetCurrentPosition = async (options: GeolocationOptions = {}): Promise<GeolocationPosition> => {
  return new Promise<GeolocationPosition>((resolve, reject) => {
    const id = Geolocation.watchPosition(options, (position, err) => {
      Geolocation.clearWatch({id});
      if(err) {
        reject(err);
        return;
      }
      resolve(position);
    });
  });
};

works perfect! thank!