Automatic Gym Check-In
The goal: when a member arrives at their gym and stays for a workout, the visit is recorded automatically, without the member opening the app. AFCore detects and records the visit; your app asks for location access, lets the member choose their gym and shows the live status.
The flow
-
Ask for location access. Visit detection needs location access while the app is in the background. Ask for location first, then for background ("Always") location, and send a member who declined to the system settings. On Android, register your activity once so AFCore can show the system prompts. APIs:
AFPermissions.registerLifecycle(Android),AFPermissions.requestPermission(FINE_LOCATION, thenBACKGROUND_LOCATION),AFPermissions.openAppSettings. -
Let the member choose their gym. Show nearby places and save the member's gym as a favorite. Fitness favorites are the places AFCore watches for visits. APIs:
locations().getLocationsByRadius,locations().addFavorite,locations().getFavorites. -
Start visit detection. One call geofences the member's gyms and starts watching for visits. When something blocks it, such as a missing permission or Location Services turned off, the result says why so your app can explain it. APIs:
locations().areLocationServicesEnabled,locations().startVisitDetection,locations().subscribeToVisitDetectionStatus. -
Show live status. Arrival, a confirmed visit and departure arrive as events, so the screen can show "You're at the gym" and then "Checked in". APIs:
locations().subscribeToVisitEvents(AFGeofenceEvent.Entered,Dwell,Exited). -
Show the visit in the member's activity. The confirmed visit appears in the member's activity calendar. A visit detected without a connection is sent automatically once the device is back online. APIs:
activities().get.
Visit detection keeps working after the app is closed or the device restarts, as long as your app calls AFCore.initialize() at launch. It belongs to the member's session: start it again after the next sign-in.
Example: start detection and show live status
- Android (Kotlin)
- iOS (Swift)
lifecycleScope.launch {
try {
val result = AFCore.locations().startVisitDetection()
if (result is AFGeofencingResult.Failure) return@launch showBlocked(result.message)
subscription = AFCore.locations().subscribeToVisitEvents(
onEvent = { event ->
runOnUiThread {
when (event) {
is AFGeofenceEvent.Entered -> showArrived(event.geofenceId)
is AFGeofenceEvent.Dwell -> showCheckedIn() // AFCore records the visit
is AFGeofenceEvent.Exited -> showWatching()
else -> Unit
}
}
}
)
} catch (t: Throwable) {
showError(t)
}
}
// When the screen goes away: subscription.close()
Task { @MainActor in
do {
let result = try await AFCore.shared.locations().startVisitDetection()
if let failure = result as? AFGeofencingResult.Failure {
showBlocked(failure.message)
return
}
subscription = AFCore.shared.locations().subscribeToVisitEvents(
onEvent: { event in
Task { @MainActor in
if let entered = event as? AFGeofenceEvent.Entered {
showArrived(entered.geofenceId)
} else if event is AFGeofenceEvent.Dwell {
showCheckedIn() // AFCore records the visit
} else if event is AFGeofenceEvent.Exited {
showWatching()
}
}
},
onError: { _ in }
)
} catch {
showError(error)
}
}
// When the screen goes away: subscription?.close()
Learn more
- Gym Visits and Locations -- the capabilities this recipe uses.
- Runtime Permissions -- the location permission flow.
- API Reference -- every call named above.
- Complete, step-by-step integration guides are in AIDE Docs.