Map
The Map feature returns marker information for facilities around a coordinate or within a viewport. Use these markers to build an interactive map experience (pins, callouts, selection). When a user adds a location to their account (via Member Facilities), that facility is eligible to be geofenced by your app's configured geofencing mechanisms, which can later notify entry, exit, and dwell events.
What you can build
- A Nearby map that shows facilities around the member’s current location.
- A Pan/Zoom map that reloads markers for the visible viewport.
- A Select & Add flow to attach a facility to the member so it can be geofenced later.
- A Details bottom sheet with distance, name, and an Add button.
Returned data: List<MapMarker?>
Each
MapMarkerrepresents a facility suitable for rendering on a map (id, name, coordinates, and optional metadata depending on your SDK version).
Permissions & threading
- Location permission is recommended for centering the map and computing distance.
- Android: request
FINE_LOCATION/BACKGROUND_LOCATIONas needed usingAFPermissions. - iOS: use
CLLocationManagerauthorization as appropriate for your app. - Call map APIs on background threads (Android
Dispatchers.IO); update UI on the main thread.
Load markers around a coordinate (Nearby)
Android (Kotlin)
suspend fun loadNearby(lat: Double, lng: Double): List<MapMarker?> =
withContext(Dispatchers.IO) {
AFCore.map().getNearbyFacilities(latitude = lat, longitude = lng)
}
// Usage from Activity/Presenter
val markers = loadNearby(memberLat, memberLng)
mapRenderer.showMarkers(markers)
iOS (Swift)
func loadNearby(lat: Double, lng: Double) async throws -> [MapMarker?] {
try await AFCore.shared.map().getNearbyFacilities(latitude: lat, longitude: lng)
}
// Usage
let markers = try await loadNearby(lat: memberLat, lng: memberLng)
MapRenderer.show(markers)
Load markers for a viewport (Pan/Zoom)
Android (Kotlin)
suspend fun loadViewport(
upperLeftLat: Double,
upperLeftLng: Double,
lowerRightLat: Double,
lowerRightLng: Double
): List<MapMarker?> = withContext(Dispatchers.IO) {
AFCore.map().getFacilitiesByViewPort(
upperLeftLat = upperLeftLat,
upperLeftLng = upperLeftLng,
lowerRightLat = lowerRightLat,
lowerRightLng = lowerRightLng
)
}
// Call this when the camera idle event fires:
val markers = loadViewport(bounds.north, bounds.west, bounds.south, bounds.east)
mapRenderer.showMarkers(markers)
iOS (Swift)
func loadViewport(_ upperLeft: CLLocationCoordinate2D,
_ lowerRight: CLLocationCoordinate2D) async throws -> [MapMarker?] {
try await AFCore.shared.map().getFacilitiesByViewPort(
upperLeftLat: upperLeft.latitude,
upperLeftLng: upperLeft.longitude,
lowerRightLat: lowerRight.latitude,
lowerRightLng: lowerRight.longitude
)
}
// On map regionDidChange (idle):
let markers = try await loadViewport(upperLeft, lowerRight)
MapRenderer.show(markers)
Add a facility so it can be geofenced
When the user taps a marker and confirms, add it to their account (Member Facilities). Once added, your configured geofencing (outside of the Map interface) can track entry/exit/dwell for that facility.
Android (Kotlin)
// From a marker selection callback
val selectedId = marker.facilityId
val added = withContext(Dispatchers.IO) {
// Mark as verified; creates or updates membership relationship
AFCore.facilities().update(facilityId = selectedId, verified = true)
}
if (added) {
// Optionally refresh the member's facilities and start monitoring elsewhere in your app
toast("Facility added for monitoring")
}
iOS (Swift)
let selectedId = marker.facilityId
let added = try await AFCore.shared.facilities().update(facilityId: selectedId, verified: true)
if added {
// Optionally refresh the member's facilities and let your app's geofencing kick in
showToast("Facility added for monitoring")
}
The Map interface does not start geofencing by itself. Adding a facility associates it with the member; your app’s geofencing components handle monitoring and notifications.
UI patterns & tips
- Camera strategy: load nearby on first open; switch to viewport loads after pan/zoom for fewer requests.
- Clustering: if your provider returns dense areas, cluster markers for readability and performance.
- Selection affordances: show a callout/bottom sheet with an Add button; disable it if the facility is already on the member’s list.
- Caching: cache last response for the current viewport to minimize flicker on small pans/zooms.
- Distance calc: compute distance on-device for sorting if helpful.
- Offline fallback: keep the last result to render something when network is unavailable.
Error handling
- Show actionable messages for network failures (“We couldn’t load locations. Pull to retry.”).
- Debounce camera-move events to avoid spamming requests.
- Validate coordinates before calling the API; handle empty result sets by showing an informative empty state.
Quick reference
// Android
AFCore.map().getNearbyFacilities(latitude, longitude)
AFCore.map().getFacilitiesByViewPort(upperLeftLat, upperLeftLng, lowerRightLat, lowerRightLng)
// Add to member for geofencing via Member Facilities
AFCore.facilities().update(facilityId, verified = true)
// iOS
try await AFCore.shared.map().getNearbyFacilities(latitude: lat, longitude: lng)
try await AFCore.shared.map().getFacilitiesByViewPort(upperLeftLat: ulLat, upperLeftLng: ulLng, lowerRightLat: lrLat, lowerRightLng: lrLng)
// Add to member for geofencing via Member Facilities
try await AFCore.shared.facilities().update(facilityId: id, verified: true)