Реалізація підсчету кроків у мобільному приложенні
Казалось би, кроки — найпростіша метрика. На справі: iOS та Android вважають кроки по-різному, телефон у кармані та в руці дає різні паттерни акселерометра, а задваивання даних між HealthKit та Google Fit — класична скарга користувачів у відзивах.
Два підходи: системний шагомер проти власного алгоритму
Системний шагомер (рекомендується)
iOS: CMPedometer — найнадійніший варіант. Вважає кроки на рівні Motion Coprocessor (M-серія), не потребує постійної роботи приложення:
let pedometer = CMPedometer()
guard CMPedometer.isStepCountingAvailable() else { return }
// Історичні дані
pedometer.queryPedometerData(from: startDate, to: endDate) { data, error in
guard let data = data else { return }
print("Кроки: \(data.numberOfSteps)")
print("Дистанція: \(data.distance ?? 0) м")
print("Поверхи вверх: \(data.floorsAscended ?? 0)")
}
// Живі оновлення
pedometer.startUpdates(from: Date()) { data, error in
DispatchQueue.main.async {
self.stepCount = data?.numberOfSteps.intValue ?? 0
}
}
CMPedometer.startUpdates() продовжує працювати навіть коли приложення в фоне — дані накопичуються та приходять при наступному відкритті. Батарея не тратиться на високочастотний опрос — все на рівні залізо.
Android: TYPE_STEP_COUNTER та TYPE_STEP_DETECTOR.
TYPE_STEP_COUNTER — накопичуючий лічильник з моменту останної перезагрузки пристрою. Скидається при reboot. Потрібно зберігати базове значення при першому запуску дня.
TYPE_STEP_DETECTOR — подія на кожний крок. Для підсчету в реальному часі.
val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
val stepSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
val stepListener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
val totalSteps = event.values[0].toLong()
// Віднімаємо базове значення, отримане при першому запуску
val todaySteps = totalSteps - baseStepCount
updateUI(todaySteps)
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
}
sensorManager.registerListener(stepListener, stepSensor, SensorManager.SENSOR_DELAY_NORMAL)
SENSOR_DELAY_NORMAL — достатньо для шагомера. SENSOR_DELAY_FASTEST тут бессмисленний та убиває батарею.
Власний алгоритм (коли потрібен)
Системний шагомер не працює на деяких бюджетних Android-пристроях без TYPE_STEP_COUNTER (рідко, але зустрічається). У цьому випадку — Peak Detection на акселерометрі:
- Читаємо
TYPE_ACCELEROMETERна 25 Гц - Вичисляємо величину:
sqrt(x² + y² + z²) - Застосовуємо low-pass фільтр:
filtered = alpha * raw + (1 - alpha) * prev(alpha ≈ 0.1) - Детектуємо пік:
filtered > threshold(зазвичай 10.5–11.5 м/с²) після переходу через baseline - Мінімальний інтервал між кроками: 250–400 мс
Точність власного алгоритму — 85–92% проти 98%+ системного. Для більшості фітнес-приложень системного достатньо.
Інтеграція з HealthKit / Health Connect
Кроки потрібно писати в платформене сховище, інакше вони не з'являться в системному приложенні «Здоров'я» (iOS) або Health Connect (Android).
iOS — запис в HealthKit:
let stepType = HKQuantityType(.stepCount)
let stepSample = HKQuantitySample(
type: stepType,
quantity: HKQuantity(unit: .count(), doubleValue: Double(steps)),
start: periodStart,
end: periodEnd
)
healthStore.save(stepSample) { success, error in }
Android — Health Connect:
val stepsRecord = StepsRecord(
startTime = periodStart,
startZoneOffset = ZoneOffset.UTC,
endTime = periodEnd,
endZoneOffset = ZoneOffset.UTC,
count = steps
)
healthConnectClient.insertRecords(listOf(stepsRecord))
Проблема задваивання
Якщо телефон передає дані в Google Fit, а приложение пише їх ще й в Health Connect — користувач бачить подвійну кількість кроків у системних приложеннях. Рішення: не писати кроки самостійно, якщо дозволено читання з системного шагомера. Читаємо з системного джерела, агрегуємо, показуємо у своєму UI, в HealthKit/Health Connect НЕ пишемо (або пишемо з унікальним source identifier та попереджуємо користувача про можливе дублювання).
Терміни
Реалізація лічильника кроків з системним шагомером на одній платформі — 2–4 робочих дня. З інтеграцією в HealthKit/Health Connect, фоновою синхронізацією та вджетом — до 2 тижнів.







