Self-Report
Self-Report allows members to manually log facility visits when automatic detection (geofencing or beacon proximity) was not possible -- for example, when the member forgot to enable location services or visited a facility without beacons.
Data Model
SelfReportData
Returned by get(), this contains the member's facilities eligible for self-reporting and their past self-report history.
| Field | Type | Description |
|---|---|---|
facilities | List<Facility> | Facilities the member can self-report against. |
selfReportMonths | List<SelfReportMonth> | Monthly breakdown of past self-reports. |
statusMessage | String? | Optional server message (e.g., daily limit reached). |
Each SelfReportVisit references its facility by facilityId only -- resolve display details by matching that id against SelfReportData.facilities.
SelfReportMonth
One calendar month of self-reported visits, grouped so you can render them onto a per-month calendar.
| Field | Type | Description |
|---|---|---|
month | Int? | 1-based month number (1 = January, 12 = December), or null when omitted. |
year | Int? | Four-digit year, or null when omitted. |
visits | List<SelfReportVisit> | The self-reported visits that fall within this month. |
SelfReportVisit
A single self-reported visit. Use these fields to drive day-level blocking in the UI (see Product Constraints).
| Field | Type | Description |
|---|---|---|
day | Int? | 1-based day of the month (1--31), or null when omitted. Combine with the parent SelfReportMonth's month/year to form the full date. |
statusType | ActivityStatus | Review status of the visit -- typically PENDING, APPROVED/ACCEPTED, or REJECTED. Defaults to PENDING. |
facilityId | Int? | The reported facility's id, matched against SelfReportData.facilities. |
A consumer determines whether a given day already has a blocking report by scanning selfReportMonths[].visits[] for a visit whose day (plus the month's month/year) matches the target date, then inspecting its statusType.
Get Self-Report Data
Retrieve the member's eligible facilities and self-report history.
- Android (Kotlin)
- iOS (Swift)
lifecycleScope.launch {
val data = AFCore.selfReport().get()
val facilities = data.facilities
val history = data.selfReportMonths
// Populate a facility picker and display past reports
}
let data = try await AFCore.shared.selfReport().get()
let facilities = data.facilities
let history = data.selfReportMonths
Submit a Self-Report
Log a manual visit for a specific facility.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
facilityId | Int | Yes | The facility where the visit occurred. |
timestampInSeconds | Long | Yes | UTC epoch timestamp of the visit. |
timeSpentInMinutes | Int | Yes | Duration of the visit in minutes. |
latitude | Double? | No | Device latitude at time of report. |
longitude | Double? | No | Device longitude at time of report. |
atFacility | Boolean | No | Whether the member is currently at the facility (default false). |
- Android (Kotlin)
- iOS (Swift)
lifecycleScope.launch {
val result = AFCore.selfReport().submit(
facilityId = 42,
timestampInSeconds = System.currentTimeMillis() / 1000,
timeSpentInMinutes = 60,
latitude = 32.7157,
longitude = -117.1611,
atFacility = true
)
if (result.status) {
showSuccess("Visit reported successfully")
} else {
showError("Could not submit report: ${result.statusMessage}")
}
}
let result = try await AFCore.shared.selfReport().submit(
facilityId: 42,
timestampInSeconds: Int64(Date().timeIntervalSince1970),
timeSpentInMinutes: 60,
latitude: 32.7157,
longitude: -117.1611,
atFacility: true
)
if result.status {
showSuccess("Visit reported successfully")
} else {
showError("Could not submit report: \(result.statusMessage ?? "Unknown error")")
}
submit is offline-aware. When the device has no connectivity, the report is persisted to the SDK's offline outbox and delivered automatically once the network returns -- no manual retry needed. In that case the returned AFResult has status == true and queued == true. Read result.queued if you want to tell the member their report was "queued for later delivery" versus "submitted online."
Delete a Self-Report
Remove a previously submitted self-report for a given date.
- Android (Kotlin)
- iOS (Swift)
lifecycleScope.launch {
val result = AFCore.selfReport().delete("2026-02-13")
if (result.status) {
showSuccess("Report deleted")
}
}
let result = try await AFCore.shared.selfReport().delete("2026-02-13")
if result.status {
showSuccess("Report deleted")
}
Product Constraints
Self-reporting is gated per day. Enforce these rules in your UI before showing the submit affordance for a given date:
- An approved (or partial) non-walking visit blocks the day. If the member already has an approved or partially-approved visit that is not a SmartWalking entry on that day, new self-reports for that day are not allowed.
- A pending self-report blocks new ones. While a self-report for a day is still
PENDING, the member cannot submit another for that day -- they must delete the pending one first. - A SmartWalking-only day does NOT block. A day whose only activity is SmartWalking step credit remains open for self-reporting.
You can derive day-level blocking from the data returned by get(): scan each SelfReportMonth.visits for a visit matching the target day, and treat it as blocking when its statusType is PENDING, APPROVED, or ACCEPTED.
- Android (Kotlin)
- iOS (Swift)
lifecycleScope.launch {
val data = AFCore.selfReport().get()
fun isDayBlocked(year: Int, month: Int, day: Int): Boolean =
data.selfReportMonths
.filter { it.year == year && it.month == month }
.flatMap { it.visits }
.any { visit ->
visit.day == day && visit.statusType in setOf(
ActivityStatus.PENDING,
ActivityStatus.APPROVED,
ActivityStatus.ACCEPTED
)
}
}
let data = try await AFCore.shared.selfReport().get()
func isDayBlocked(year: Int32, month: Int32, day: Int32) -> Bool {
data.selfReportMonths
.filter { $0.year == year && $0.month == month }
.flatMap { $0.visits }
.contains { visit in
visit.day == day &&
[.pending, .approved, .accepted].contains(visit.statusType)
}
}
Best Practices
- Confirm before submitting. Show a confirmation dialog with the facility name, date, and duration before calling
submit. - Distinguish manual from automatic visits in the UI. Use different icons or labels so members understand which visits were detected automatically and which were self-reported.
- Respect server-side limits. Some organizations limit the number of self-reports per day or month. Check
statusMessagefor guidance.
Quick Reference
- Android (Kotlin)
- iOS (Swift)
AFCore.selfReport().get()
AFCore.selfReport().submit(facilityId, timestampInSeconds, timeSpentInMinutes, lat, lng, atFacility)
AFCore.selfReport().delete(date)
try await AFCore.shared.selfReport().get()
try await AFCore.shared.selfReport().submit(facilityId:timestampInSeconds:timeSpentInMinutes:latitude:longitude:atFacility:)
try await AFCore.shared.selfReport().delete("YYYY-MM-DD")