Locations
Locations feature - the member's relationship with places: favorite locations (gyms and retail stores the member chose), map search by viewport or radius, the nomination surface (nominating a missing place, reporting a problem with an existing one, the member's own submissions, the category and type catalogs a nomination form needs, place search and recent searches), automatic gym-visit detection and location sharing.
Reach it with AFCore.locations() (Kotlin) or AFCore.shared.locations() (Swift). It supersedes the deprecated AFCore.facilities() and AFCore.map(), which keep working.
Moving from facilities() and map()
| Deprecated | Use instead |
|---|---|
facilities().get() | getFavorites; filter gyms with FavoriteLocation.isFitness |
facilities().update(id, verified = true) | addFavorite; isPrimary = true makes it the primary gym |
facilities().update(id, verified = false) | No replacement yet: keep calling it to exclude a location (muteFavorite is a pause, not an exclusion) |
facilities().delete(id) | removeFavorite; the legacy club id is accepted |
facilities().startMonitoring() / stopMonitoring() / isMonitoringActive() | startVisitDetection / stopVisitDetection / isVisitDetectionActive |
facilities().geofenceEvents / subscribeToEvents() | visitEvents / subscribeToVisitEvents |
facilities().subscribeToStatus() | subscribeToVisitDetectionStatus |
facilities().areLocationServicesEnabled() / openLocationSettings() / openAppSettings() | areLocationServicesEnabled / openLocationSettings / openAppSettings |
facilities().nominateFacility(...) | nominateLocation, with a category, a type and a Google place id |
facilities().getNominatedFacilities() | getMySubmissions: nominations and problem reports, paged, with their review status |
map().getNearbyFacilities(...) | getLocationsByRadius |
map().getFacilitiesByViewPort(...) | getLocationsByViewPort |
AFCore.heartbeat / AFCore.heartbeatEnabled | locationSharing / locationSharingEnabled |
AFCore.setHeartbeatEnabled(), AFCoreConfig.Builder.heartbeatEnabled() | AFCoreConfig.Builder.locationSharingEnabled(true) |
AFCore.fireDebugHeartbeat() | shareLocationNow |
With no predecessor: muteFavorite / unmuteFavorite, reportLocationProblem, searchPlaces, the recent place searches (getRecentPlaceSearches, rememberPlaceSearch, clearRecentPlaceSearches) and the nomination catalogs (getLocationCategories, getLocationTypes).
Error contract
Every function is suspend and throws on failure - network, HTTP and malformed-response errors surface as AFException (with the underlying cause preserved), never as an empty result. An empty list always means the member genuinely has no favorites.
No offline queue
Locations writes - favorites, nominations and problem reports - are never queued offline. Without a connection they throw; catch the error and let the member retry.
Mutations return the fresh list
addFavorite, muteFavorite and unmuteFavorite return the member's complete, post-mutation favorites list as the server sends it - use it to reconcile local state in one pass instead of refetching. removeFavorite is the exception: the server answers 204 with no body.
Presence: visit detection and location sharing
Two independent capabilities, each with its own consent:
Visit detection (startVisitDetection) credits gym visits automatically: the device geofences the member's fitness favorites and posts each visit. It needs "Always" location permission and is the successor of
facilities().startMonitoring().Location sharing (locationSharing) sends the member's location to the server, which evaluates every place - gyms, retail stores, proximity zones - on its side: offers near a store, journeys, and server-decided visits. It is opt-in through
AFCoreConfig.Builder.locationSharingEnabled, never implied by visit detection, and keeps sending in the background when the device moves significantly.
The SDK always sends what it observes; the server decides what it counts. Both are iOS and Android features: on watchOS startVisitDetection throws and shareLocationNow reports a suppressed attempt without sending.
Identifier semantics
Mutations are addressed by the canonical FavoriteLocation.locationId. The server also resolves a FavoriteLocation.legacyClubId passed in its place and answers 409 when the number matches both spaces ambiguously - that 409 surfaces as an AFException with httpCode = 409.
Calling from Swift
Kotlin default arguments do not reach Swift: pass every argument, nil for an optional one (getLocationTypes(categoryId: nil, categoryCode: nil, activeOnly: true)), and every constructor parameter of a model such as AdvancedLocationFilter. A Kotlin Long is an Int64 and an Int an Int32; an optional number is boxed (KotlinInt(int: 7), KotlinDouble(value: 40.7)), and a suspend function or a callback that carries a Boolean hands over a KotlinBoolean (read .boolValue). The subscribeTo... callbacks run on a background thread - hop to the main actor before touching UI.
Android (Kotlin)
lifecycleScope.launch {
try {
// Favorites, muted or not; EXCLUDED is the member's blacklist.
val favorites = AFCore.locations().getFavorites()
.filter { it.favoriteType != LocationFavoriteType.EXCLUDED }
showFavorites(favorites)
} catch (t: Throwable) {
showError(t)
}
}iOS (Swift)
Task {
do {
let favorites = try await AFCore.shared.locations().getFavorites()
.filter { $0.favoriteType != .excluded }
self.showFavorites(favorites)
} catch {
self.showError(error)
}
}Properties
The most recent location-sharing attempt - sent, suppressed (and why), or failed - or null before the first one in this process. The SDK shares on initialization, on every foreground, alongside each visit-detection geofence transition, and in the background whenever the device moves significantly (AFHeartbeatTrigger.SIGNIFICANT_LOCATION_CHANGE). The background trigger runs only when the opt-in came from AFCoreConfig.Builder.locationSharingEnabled(true); an opt-in made with the deprecated heartbeat switches keeps the 1.11 triggers and never shares in the background.
Whether location sharing is on for this install - the opt-in declared with AFCoreConfig.Builder.locationSharingEnabled. Off by default: a host must collect the member's consent to share a live location separately from location permission.
Visit-detection events at the member's gyms: Enter, Exit, Dwell and the session's registration / error events.
Functions
Adds a location to the member's favorites. The server treats this as a declarative upsert: an excluded location becomes a favorite and a muted one is unmuted - it never answers 409 for a location already favorited.
Whether the device's system-level Location Services switch is on.
Forgets every remembered place search.
Retrieves all of the member's favorite locations, each joined with the location's own data (address, coordinates, category, geofence radius), so a list never needs a second fetch per row.
Lists the categories a nomination can be filed under, driving the form's category picker.
Retrieves advanced location markers within a radius of a center point - the full place record, filterable by category, type, amenity and status.
Retrieves advanced location markers within bounds - the same query as getLocationsByViewPort, addressed by a south-west / north-east box.
Retrieves advanced location markers within a viewport rectangle - the full place record, filterable by category, type, amenity and status.
Lists the place types a nomination can be filed under, each linked to its category.
Lists what the member has submitted - nominated places and reported problems - merged into one page ordered by creation date. Rows are the same com.advantahealth.api.facilities.model.MemberSubmission shape the facilities feed renders, so one list serves both.
The member's recent place searches, most recent first.
Whether a monitoring session started with startVisitDetection is currently ACTIVE.
Mutes a favorite location - a pause, not an opt-out: it stays in the member's list, but produces neither notifications nor automatic visits until it is unmuted. A running visit-detection session drops its geofence. (Blacklisting is LocationFavoriteType.EXCLUDED, not muting.)
Nominates a new place on behalf of the member.
Opens this app's settings page, where location permission is granted.
Opens the system Location Services settings.
Records a place the member chose, moving it to the front if it is already remembered.
Removes a location from the member's favorites.
Reports a problem about an existing location, carrying the member's corrections as a structured patch (see LocationProblemReport).
Searches places matching the query text, optionally biased by a coordinate circle and a category, so a nomination can carry a canonical place id instead of a hand-typed name.
Shares the member's current location right away, outside the automatic triggers - for example after the member opens a map. The opt-in, login, permission and location-fix gates still apply; the throttle does not.
Starts visit detection: registers a geofence around each of the member's gym favorites and credits automatic visits from them, in the background and across app restarts. Replaces any session already running, including one started with facilities().startMonitoring(). When location sharing is on, each geofence transition also sends the member's location.
Stops visit detection and removes every registered geofence. Location sharing is unaffected.
Subscribes to locationSharing with callbacks - the Swift-friendly form. Replays the latest attempt, then each new one; nothing is delivered before the first attempt.
Subscribes to locationSharingEnabled with callbacks - for binding a SwiftUI toggle to the opt-in. Replays the current value, then each change.
Subscribes to the monitoring session status. Replays the current value, then emits each distinct change - for example PAUSED to ACTIVE once the member grants Always location.
Subscribes to visitEvents with callbacks - the Swift-friendly form.
Unmutes a favorite location - its notifications and automatic visits resume, and a running visit-detection session registers it again.