`навигатор.геолокация.getCurrentPosition()` зависает на iOS PWA

#javascript #ios #typescript #progressive-web-apps #w3c-geolocation

Вопрос:

У меня есть этот фрагмент:

 const getCurrentPosition = () =>
  new Promise<GeolocationPosition>((resolve, reject) => {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(resolve, reject, {
        timeout: 5000,
        maximumAge: 2000,
        enableHighAccuracy: true,
      });
    } else {
      reject(new Error('Browser does not support geolocation!'));
    }
  });
 

Который я называю так:

 try {
  const { coords } = await getCurrentPosition();
  // do stuff with coords
}
catch (e){
  // handle the error
}
 

При выполнении этого кода в iOS PWA ( navigator.standalone ), если в настройках моего местоположения для Safari ( Settings > > Privacy > > > Location Services > > Safari Websites ) установлено значение: Ask Next Time , getCurrentPosition() обещание зависает и не истекает, не разрешается или не отклоняется. Он не запрашивает у меня мое местоположение, как это происходит в браузере Safari iOS

Если я изменю настройки на Never или While Using the App , он предложит мне и будет работать нормально.

Я хочу иметь возможность обрабатывать сценарий, в котором пользователь установил свои настройки Ask Next Time при использовании PWA.

Ответ №1:

Решение этой проблемы было довольно тривиальным, как только мы с коллегами поняли это:

 export function getUserPosition(): Promise<GeolocationPosition> {
  const promiseArray = [];

  if (navigator.standalone) {
    promiseArray.push(
      new Promise((resolve, reject) => {
        const wait = setTimeout(() => {
          clearTimeout(wait);
          reject('Location has timed out');
        }, 4000);
      })
    );
  }

  const getCurrentPositionPromise = new Promise((resolve, reject) => {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(resolve, reject, {
        timeout: 5000,
        maximumAge: 2000,
        enableHighAccuracy: true,
      });
    } else {
      reject(new Error('Browser does not support geolocation!'));
    }
  });

  promiseArray.push(getCurrentPositionPromise);

  return Promise.race(promiseArray) as Promise<GeolocationPosition>;
}
 

Что здесь происходит?

  1. Мы создаем пустой массив обещаний
  2. Если навигатор идентифицирован как standalone , мы создаем обещание, что rejects через 4 секунды (это произвольное значение, максимальное время, которое вы хотите, чтобы ваши пользователи ждали), и отправляем его в поле PromiseArray.
  3. Мы создаем еще одно обещание, которое определяет позицию пользователя и подталкивает его к тому promiseArray же.
  4. Затем мы возвращаем обещание, которое возвращается после выполнения Promise.race() . Ключ здесь в том , что в promiseArray случае, если навигатора нет, будет только одно обещание standalone , и поэтому он будет работать так, как задумано. Если навигатор является автономным, то в нем будет два обещания, promiseArray и Promise.race() он будет отклонен из тайм-аута, тем самым отказавшись от getUserPosition() функции.