Разработка мобильного приложения для бегунов
Рынок беговых приложений перенасыщен: Nike Run Club, Runkeeper, Adidas Running. Новый продукт выживает за счёт специализации: корпоративные беговые клубы, приложения для конкретных событий (марафоны, трейлы), интеграция с тренерскими платформами, локальные сообщества. Технически разработка беговых приложений хорошо изучена, но детали реализации HealthKit/Health Connect и работы с GPS в фоне регулярно вызывают вопросы.
GPS-трекинг в фоне: разные ограничения на iOS и Android
На Android foreground service с уведомлением — единственный надёжный способ писать GPS в фоне. С Android 10+ нужен FOREGROUND_SERVICE_TYPE_LOCATION в манифесте. Fused Location Provider с PRIORITY_HIGH_ACCURACY и interval = 1000 мс:
class RunTrackingService : Service() {
private lateinit var fusedLocationClient: FusedLocationProviderClient
private val locationCallback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult) {
result.lastLocation?.let { location ->
if (location.accuracy < 20f) { // отбрасываем неточные точки
processLocation(location)
}
}
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startForeground(NOTIFICATION_ID, buildNotification())
val request = LocationRequest.Builder(1000L)
.setPriority(Priority.PRIORITY_HIGH_ACCURACY)
.setMinUpdateDistanceMeters(3f)
.build()
fusedLocationClient.requestLocationUpdates(request, locationCallback, mainLooper)
return START_STICKY
}
}
На iOS — CLLocationManager с allowsBackgroundLocationUpdates = true и capability Background Modes → Location updates. Важно: pausesLocationUpdatesAutomatically = false, иначе iOS остановит трекинг если телефон лежит без движения больше нескольких минут. Это критично при пробежке в гору медленным темпом — iOS думает, что пользователь остановился.
HealthKit и Health Connect: интеграция с платформенным здоровьем
На iOS данные пробежки пишем в HealthKit через HKWorkout:
func saveRun(distance: Double, duration: TimeInterval, route: [CLLocation]) async throws {
let workoutBuilder = HKWorkoutBuilder(healthStore: healthStore,
configuration: HKWorkoutConfiguration().apply {
$0.activityType = .running
$0.locationType = .outdoor
},
device: .local())
try await workoutBuilder.beginCollection(at: Date().addingTimeInterval(-duration))
let distanceSample = HKQuantitySample(
type: HKQuantityType(.distanceWalkingRunning),
quantity: HKQuantity(unit: .meter(), doubleValue: distance),
start: workoutBuilder.startDate!, end: Date()
)
try await workoutBuilder.addSamples([distanceSample])
let workout = try await workoutBuilder.finishWorkout()
// Сохраняем маршрут
let routeBuilder = HKWorkoutRouteBuilder(healthStore: healthStore, device: .local())
try await routeBuilder.insertRouteData(route)
try await routeBuilder.finishRoute(with: workout, metadata: nil)
}
На Android — Health Connect API (заменил Google Fit в 2023). ExerciseSessionRecord + DistanceRecord + RouteRecord. Требует androidx.health.connect:connect-client и разрешений WRITE_EXERCISE, WRITE_DISTANCE, WRITE_EXERCISE_ROUTE.
Темп, дистанция и автопаузировка
Темп (мин/км) — производная скорости: pace = 1000 / speed_ms_in_mps. Усредняем по скользящему окну 5-10 секунд, иначе темп скачет при каждом GPS-обновлении.
Автопауза при остановке: если скорость < 0.5 м/с в течение 5 секунд — пауза. Возобновление — при скорости > 1.5 м/с. Эти пороги нужно делать настраиваемыми — бегуны в горах на крутом подъёме могут идти пешком.
Анонс темпа голосом (каждый километр): AVSpeechSynthesizer / TextToSpeech с шаблоном «Километр {N}, темп {M} минут {S} секунд, всего {T}». Важно озвучивать при заблокированном экране — AVAudioSession.Category.playback (iOS).
Пульсометр и мощность бега
Bluetooth LE Heart Rate Profile (0x180D, 0x2A37) — стандарт для всех пульсометров (Polar, Wahoo TICKR, Garmin HRM). Разбор фрейма измерения пульса:
fun parseHeartRateMeasurement(value: ByteArray): Int {
val flags = value[0].toInt()
return if (flags and 0x01 == 0) {
value[1].toInt() and 0xFF // 8-bit value
} else {
(value[1].toInt() and 0xFF) + ((value[2].toInt() and 0xFF) shl 8) // 16-bit
}
}
Мощность бега — нишевая метрика. Stryd Pod (нагрудный датчик, BLE) отдаёт Running Power через Cycling Power Profile (0x1818). Тот же GATT-профиль, что у велосипедных ваттметров — удобно если в приложении уже есть поддержка CP.
Разработка беговое приложения с GPS-трекингом в фоне, HealthKit/Health Connect, BLE пульсометром и голосовыми анонсами: 7-10 недель на одну платформу. Кросс-платформа Flutter: 10-14 недель. Стоимость рассчитывается индивидуально.







