Facilities
The Facilities module manages the locations associated with the authenticated member -- gyms, partner sites, and other supported venues. Use it to load the member's facility list, verify or remove locations, enable geofence-based visit monitoring, and submit nominations for new facilities.
Data Model
Facility
| Field | Type | Description |
|---|---|---|
facilityId | Int? | Unique facility identifier. |
name | String? | Display name of the facility. |
address | String? | Street address. |
city | String? | City. |
state | String? | Two-letter state code. |
zipCode | String? | Postal code. |
latitude | Double? | Geographic latitude. |
longitude | Double? | Geographic longitude. |
verified | Boolean? | Whether the member has confirmed this facility. |
Get Facilities
Retrieve all facilities currently associated with the member. This includes verified, pending, and blacklisted locations.
- Android (Kotlin)
- iOS (Swift)
lifecycleScope.launch {
try {
val facilities = AFCore.facilities().get()
facilitiesAdapter.submitList(facilities.filterNotNull())
} catch (e: Exception) {
showError("Could not load facilities: ${e.message}")
}
}
do {
let facilities = try await AFCore.shared.facilities().get()
self.facilities = facilities.compactMap { $0 }
self.tableView.reloadData()
} catch {
showError("Could not load facilities: \(error)")
}
Verify or Blacklist a Facility
Update the verification status of a facility. Pass verified: true to confirm a location, or verified: false to blacklist it. This also works for facilities discovered through the Map search -- calling update on a facility not yet in the member's list will add it.
- Android (Kotlin)
- iOS (Swift)
lifecycleScope.launch {
val result = AFCore.facilities().update(facilityId = 12345, verified = true)
if (result.status) {
showSuccess("Facility verified")
refreshFacilities()
} else {
showError("Update failed: ${result.statusMessage}")
}
}
let result = try await AFCore.shared.facilities().update(facilityId: 12345, verified: true)
if result.status {
showSuccess("Facility verified")
refreshFacilities()
} else {
showError("Update failed: \(result.statusMessage ?? "Unknown error")")
}
Delete a Facility
Remove a facility from the member's list. This permanently detaches the location from their account.
- Android (Kotlin)
- iOS (Swift)
lifecycleScope.launch {
val result = AFCore.facilities().delete(facilityId = 12345)
if (result.status) {
removeFromList(12345)
} else {
showError("Could not remove facility: ${result.statusMessage}")
}
}
let result = try await AFCore.shared.facilities().delete(facilityId: 12345)
if result.status {
removeFromList(12345)
} else {
showError("Could not remove facility: \(result.statusMessage ?? "Unknown error")")
}
Nominate a Facility
Submit a new facility that is not yet part of the network. Use this when a member visits a gym or location that the system does not recognize.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
clubName | String | Yes | Name of the facility. |
address1 | String | Yes | Primary street address. |
address2 | String? | No | Suite, unit, or apartment number. |
city | String | Yes | City. |
stateCode | String | Yes | Two-letter state code. |
zipCode | String | Yes | Postal or ZIP code. |
latitude | Double? | No | Geographic latitude. |
longitude | Double? | No | Geographic longitude. |
phoneNumber | String? | No | Contact phone number. |
email | String? | No | Contact email address. |
managerName | String? | No | Facility manager's name. |
currentlyAMember | Boolean | No | Whether the member belongs to this facility. |
metadata | Map<String, String>? | No | Additional key-value pairs (see below). |
Metadata Keys
| Key | Description |
|---|---|
website | Canonical website URL. |
googlePlaceId | Google Places ID for programmatic verification. |
yelpId | Yelp business identifier. |
foursquareId | Foursquare venue identifier. |
source | Origin of the nomination (e.g., "ios-app", "android-app"). |
Including provider IDs makes backend verification and deduplication more reliable than free-text matching alone.
- Android (Kotlin)
- iOS (Swift)
lifecycleScope.launch {
try {
val result = AFCore.facilities().nominateFacility(
clubName = "Downtown Fitness",
address1 = "123 Main St",
address2 = null,
city = "Springfield",
stateCode = "CA",
zipCode = "90210",
latitude = 34.052235,
longitude = -118.243683,
phoneNumber = "(555) 123-4567",
email = "info@downtownfitness.example",
managerName = "Jane Doe",
currentlyAMember = true,
metadata = mapOf(
"googlePlaceId" to "ChIJN1t_tDeuEmsRUsoyG83frY4",
"source" to "android-app"
)
)
if (result.status) {
showSuccess("Nomination submitted")
} else {
showError("Nomination failed: ${result.statusMessage}")
}
} catch (e: Exception) {
showError("Could not submit nomination: ${e.message}")
}
}
do {
let result = try await AFCore.shared.facilities().nominateFacility(
clubName: "Downtown Fitness",
address1: "123 Main St",
address2: nil,
city: "Springfield",
stateCode: "CA",
zipCode: "90210",
latitude: 34.052235,
longitude: -118.243683,
phoneNumber: "(555) 123-4567",
email: "info@downtownfitness.example",
managerName: "Jane Doe",
currentlyAMember: true,
metadata: [
"googlePlaceId": "ChIJN1t_tDeuEmsRUsoyG83frY4",
"source": "ios-app"
]
)
if result.status {
showSuccess("Nomination submitted")
} else {
showError("Nomination failed: \(result.statusMessage ?? "Unknown error")")
}
} catch {
showError("Could not submit nomination: \(error)")
}
Geofence Monitoring
Enable or disable geofence-based monitoring for the member's verified facilities. When active, the SDK tracks proximity and detects visits automatically. Use subscribeToEvents() to receive real-time geofence events (see Gym Visits for full details).
Start monitoring after login or onboarding. Stop monitoring on logout or when background location tracking is no longer needed.
Start Monitoring
- Android (Kotlin)
- iOS (Swift)
// Android
lifecycleScope.launch {
val started = AFCore.facilities().startMonitoring()
if (started) {
Log.d("Facilities", "Geofence monitoring active")
}
}
// iOS
let started = try await AFCore.shared.facilities().startMonitoring()
if started {
print("Geofence monitoring active")
}
Stop Monitoring
- Android (Kotlin)
- iOS (Swift)
// Android
val stopped = AFCore.facilities().stopMonitoring()
// iOS
let stopped = try await AFCore.shared.facilities().stopMonitoring()
Check Monitoring Status
- Android (Kotlin)
- iOS (Swift)
// Android
val active = AFCore.facilities().isMonitoringActive()
// iOS
let active = try await AFCore.shared.facilities().isMonitoringActive()
Best Practices
- Cache facilities locally and refresh when the member opens the facilities screen or pulls to refresh.
- Show a confirmation dialog before deleting a facility, since the operation is permanent.
- Use optimistic UI updates when verifying or blacklisting -- update the local list immediately and revert if the API call fails.
- Validate nomination inputs client-side before submitting. Require at minimum: club name, address, city, state, and ZIP code.
Quick Reference
- Android (Kotlin)
- iOS (Swift)
// Android
AFCore.facilities().get()
AFCore.facilities().update(facilityId, verified)
AFCore.facilities().delete(facilityId)
AFCore.facilities().nominateFacility(clubName, address1, address2, city, stateCode, zipCode, ...)
AFCore.facilities().getNominatedFacilities()
AFCore.facilities().startMonitoring()
AFCore.facilities().stopMonitoring()
AFCore.facilities().isMonitoringActive()
AFCore.facilities().subscribeToEvents(onEvent, onError)
// iOS
try await AFCore.shared.facilities().get()
try await AFCore.shared.facilities().update(facilityId:verified:)
try await AFCore.shared.facilities().delete(facilityId:)
try await AFCore.shared.facilities().nominateFacility(clubName:address1:address2:city:stateCode:zipCode:...)
try await AFCore.shared.facilities().getNominatedFacilities()
try await AFCore.shared.facilities().startMonitoring()
try await AFCore.shared.facilities().stopMonitoring()
try await AFCore.shared.facilities().isMonitoringActive()
AFCore.shared.facilities().subscribeToEvents(onEvent:onError:)