Get Device Location

Retrieves the current geographical coordinates of the user's device.

Description

The getDeviceLocation() action fetches the latitude and longitude of the user. This is used to provide personalized content based on the user's current position, such as identifying the closest retail branch or providing localized currency/shipping estimates.

Function Signature

Superfans.actions.getDeviceLocation()

Structure of Response

StatusResponse
Success{ "status": "success", "message": "Action executed successfully" }
Cart not accessible{ "status": "error", "errorId": 400, "message": "Action not executed"}
Unexpected Error{ "status": "error", "errorId": 500, "errorHandle": "unknown-error", "message": "Something unexpected happened" }

Example Usage

// 1. Your Store Data
const MY_STORES = [
    { name: "City Center Branch", lat: 13.0827, lng: 80.2707, address: "Main Road, Plaza 1" },
    { name: "Riverside Store", lat: 13.0489, lng: 80.2586, address: "Second Ave, Mall 2" },
    { name: "Suburban Outlet", lat: 12.9171, lng: 80.1923, address: "Old Highway 5" },
    { name: "City Center Branch", lat: 13.0827, lng: 80.2707, address: "Main Road, Plaza 1" }
];
// 2. Haversine Formula (Calculates real-world distance in KM)
function getDistance(lat1, lon1, lat2, lon2) {
    const R = 6371; // Earth's radius in km
    const dLat = (lat2 - lat1) * Math.PI / 180;
    const dLon = (lon2 - lon1) * Math.PI / 180;
    const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
              Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
              Math.sin(dLon / 2) * Math.sin(dLon / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c; 
}
async function locateStore() {
    const container = document.getElementById('result-container');
    const loader = document.getElementById('btn-loader');
    const btnLabel = document.querySelector('.btn-label');
    // UI State: Loading
    btnLabel.classList.add('hidden');
    loader.classList.remove('hidden');
    try {
        // Step 1: Ensure Permission
        const perm = Superfans.variables.device.permissions.location;
        if (perm !== "granted") {
            await Superfans.actions.requestLocationPermission();
        }
        // Step 2: Get Location
        const loc = await Superfans.actions.getDeviceLocation();
        if (loc) {
            const uLat = loc.latitude;
            const uLng = loc.longitude;
            // Step 3: Calculate Distances & Filter by 5km
            const nearbyStores = MY_STORES.map(store => ({
                ...store,
                dist: getDistance(uLat, uLng, store.lat, store.lng)
            }))
            .filter(store => store.dist <= 5) // <--- 5km Constraint
            .sort((a, b) => a.dist - b.dist);
            // Step 4: Render Result
            if (nearbyStores.length > 0) {
                const nearest = nearbyStores[0];
                container.innerHTML = `
                    <div class="store-found-card">
                        <span class="dist-tag">${nearest.dist.toFixed(1)} km away</span>
                        <h4>${nearest.name}</h4>
                        <p>${nearest.address}</p>
                    </div>
                `;
            } else {
                container.innerHTML = `<p style="color:#FF3B30;">No stores found within 5km.</p>`;
            }
        }
    } catch (err) {
        Superfans.actions.showToast({ title: "Error", message: "Could not access GPS." });
    } finally {
        btnLabel.classList.remove('hidden');
        loader.classList.add('hidden');
    }
}
:root {
  --primary: #007AFF;
  --bg: #F2F2F7;
  --card: #FFFFFF;
  --text: #1C1C1E;
  --subtext: #8E8E93;
}
.locator-wrapper {
  padding: 20px;
  background-color: var(--bg);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.locator-card {
  background: var(--card);
  border-radius: 20px;
  padding: 24px;
  box-shadow: 0 10px 25px rgba(0,0,0,0.05);
  text-align: center;
}
.locator-header {
  display: flex;
  align-items: center;
  text-align: left;
  gap: 12px;
  margin-bottom: 20px;
}
.icon-circle {
  width: 44px;
  height: 44px;
  background: #E5F1FF;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
}
.header-text h3 { margin: 0; font-size: 18px; color: var(--text); }
.header-text p { margin: 2px 0 0; font-size: 13px; color: var(--subtext); }
.result-container {
  min-height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #F2F2F7;
  border-radius: 12px;
  margin-bottom: 20px;
  padding: 15px;
}
.store-found-card {
  text-align: left;
  width: 100%;
}
.store-found-card h4 { margin: 0; color: var(--primary); font-size: 17px; }
.store-found-card p { margin: 5px 0; color: var(--text); font-size: 14px; }
.dist-tag { 
  display: inline-block; 
  background: #E5FFE9; 
  color: #34C759; 
  padding: 4px 8px; 
  border-radius: 6px; 
  font-weight: bold; 
  font-size: 12px;
}
.find-btn {
  width: 100%;
  background: var(--primary);
  color: white;
  border: none;
  padding: 16px;
  border-radius: 12px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: opacity 0.2s;
}
.find-btn:active { opacity: 0.8; }
.loader {
  width: 20px;
  height: 20px;
  border: 3px solid rgba(255,255,255,0.3);
  border-radius: 50%;
  border-top-color: #fff;
  animation: spin 1s ease-in-out infinite;
  margin: 0 auto;
}
@keyframes spin { to { transform: rotate(360deg); } }
.hidden { display: none; }
<div class="locator-wrapper">
  <div class="locator-card">
    <div class="locator-header">
      <div class="icon-circle">📍</div>
      <div class="header-text">
        <h3>Store Locator</h3>
        <p id="status-text">Find the nearest branch within 5km</p>
      </div>
    </div>
    <div id="result-container" class="result-container">
      <div class="empty-state">
        <p>Click below to find a store near you.</p>
      </div>
    </div>
    <button id="find-btn" class="find-btn" onclick="locateStore()">
      <span class="btn-label">Find Nearest Store</span>
      <div id="btn-loader" class="loader hidden"></div>
    </button>
  </div>
</div>
<div id="device_location"></div>

Best Practices

  1. Check Permission State First: Always verify the current permission status using VajroSDK.variables.device.permissions.location. If it's denied, show a custom UI with instructions on how to enable it in Settings rather than calling the action and getting an error.
  2. Use Lazy Loading: Only call getDeviceLocation() when the user performs a specific action that requires it, such as clicking a "Use Current Location" button. Avoid calling it automatically on every page load to save battery and respect privacy.

Caveats

  1. Accuracy Variance: The precision of the coordinates (latitude/longitude) depends on whether the device is using GPS, Wi-Fi, or cellular towers. Indoor locations may return a significantly larger "accuracy radius."
  2. User Permission: Unlike some web APIs, this requires the app-level permission to be granted. If the user selects "Ask Next Time" or "Never," the action will redirect the user to get permission.