Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> HTML

Mobile Safari xác định khi nào prompt chia sẻ vị trí (Geolocation)

Mobile Safari (iOS) có hành vi đặc biệt với Geolocation prompt. Hiểu cơ chế giúp UX tốt hơn, tránh bị chặn hoặc prompt lặp lại.

Khi nào Safari hiển thị prompt?

  1. Lần đầu tiên trang web gọi getCurrentPosition() hoặc watchPosition().
  2. User chưa từng cho phép trang web này truy cập vị trí.
  3. Trang web được phục vụ qua HTTPS (hoặc localhost).
  4. Có tương tác người dùng (click, tap) trước khi gọi API (best practice, bắt buộc cho một số API khác).

Logic xử lý thông minh (Best Practice)

var firstTime = true; // Flag lần đầu truy cập
var userInLocation = false; // Flag user ở vùng quan tâm

function checkLocation() {
  if (firstTime) {
    // Lần đầu: xin permission + check vị trí
    navigator.geolocation.getCurrentPosition(function(position) {
      var coords = position.coords;
      if (isInTargetArea(coords.latitude, coords.longitude)) {
        storeCoordsForUser(coords);
        userInLocation = true;
        // Bắt đầu theo dõi liên tục
        navigator.geolocation.watchPosition(onWatchSuccess, onWatchError, { enableHighAccuracy: true });
      } else {
        // User ngoài vùng -> không watch, có thể hỏi lại sau
        userInLocation = false;
      }
      firstTime = false;
    }, function(error) {
      // Xử lý lỗi: denied, timeout, unavailable
      firstTime = false;
    }, { enableHighAccuracy: true, timeout: 10000 });
  } 
  else if (userInLocation) {
    // Đã biết user ở vùng -> tiếp tục watch
    navigator.geolocation.watchPosition(onWatchSuccess, onWatchError, { enableHighAccuracy: true });
  }
  // Else: user không ở vùng -> không làm gì
}

function isInTargetArea(lat, lng) {
  // Kiểm tra trong bounding box hoặc radius
  var target = { lat: 21.0285, lng: 105.8542 }; // Hà Nội
  var radiusKm = 50;
  // Haversine formula...
  return distance(lat, lng, target.lat, target.lng) <= radiusKm;
}

Quy tắc quan trọng (iOS Safari)

  • User gesture required: Gọi geolocation trong event handler (click, touchend), không gọi ngay khi load trang (trừ khi đã có permission).
  • Permission persistence: Safari nhớ chọn "Allow" / "Deny" cho từng origin. "Deny" vĩnh viễn trừ khi user vào Settings > Safari > Location.
  • watchPosition() sau khi đã có permission không prompt lại.
  • Background tabs: iOS tạm dừng geolocation khi tab không active. Dùng Page Visibility API để resume.

Xử lý "Permission Denied" gracefully

function handleGeoError(error) {
  switch(error.code) {
    case error.PERMISSION_DENIED:
      showMessage('Bạn đã từ chối chia sẻ vị trí. Vui lòng bật trong Settings > Safari > Location > [Website] > Ask/Allow.');
      break;
    case error.POSITION_UNAVAILABLE:
      showMessage('Không xác định được vị trí. Bật GPS/WiFi.');
      break;
    case error.TIMEOUT:
      showMessage('Hết thời gian chờ. Thử lại.');
      break;
  }
}

Lưu ý iOS 16+ / iPadOS

  • Precision location (Precise: On/Off) - User có thể chọn chia sẻ vị trí chính xác hoặc gần đúng (~10km).
  • App web (PWA) added to Home Screen: Quyền location riêng biệt với Safari tab.