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?
- Lần đầu tiên trang web gọi
getCurrentPosition()hoặcwatchPosition(). - User chưa từng cho phép trang web này truy cập vị trí.
- Trang web được phục vụ qua HTTPS (hoặc localhost).
- 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.