Розробка мобільного додатку для бігунів
GPS-трекінг у фоні на iOS може зупинитися, якщо бігун уповільнився на підйомі — iOS думає, що користувач стоїть. Рішення — вимкнути pausesLocationUpdatesAutomatically. Але це лише одна з десятка технічних грабель, які чекають при створенні бігового додатку. За більш ніж 5 років ми реалізували понад 20 проєктів для корпоративних бігових клубів, спортивних подій та незалежних тренерів. Типові проблеми: некоректна автопауза, втрата GPS-треку в тунелях, конфлікт з енергозбереженням, інтеграція HealthKit/Health Connect. Ми підготували чек-лист та готові модулі, які скорочують час розробки на 30–40%. Пропонуємо розробку бігового додатку під ключ. Зв'яжіться з нами для безкоштовної оцінки — надішлемо приклади коду та архітектуру.
Ринок бігових додатків перенасичений: Nike Run Club, Runkeeper, Adidas Running. Новий продукт виживає завдяки спеціалізації: корпоративні бігові клуби, додатки для конкретних подій (марафони, трейли), інтеграція з тренерськими платформами, локальні спільноти. Готові модулі для GPS, HealthKit та Health Connect економлять до 2 тижнів розробки на кожну інтеграцію — це в 2 рази швидше за самостійну реалізацію. Для порівняння: самостійна реалізація аналогічної функціональності коштує $5000–$7000, тому використання наших модулів забезпечує економію в 2 рази.
Як працює GPS-трекінг у фоні на iOS та Android?
На iOS — CLLocationManager з allowsBackgroundLocationUpdates = true та capability Background Modes → Location updates. Важливо: pausesLocationUpdatesAutomatically = false, інакше iOS зупинить трекінг якщо телефон лежить без руху більше кількох хвилин. Це критично при пробіжці в гору повільним темпом.
На 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 | Android |
|---|---|---|
| Механізм | CLLocationManager + Background Modes | Foreground Service + Fused Location Provider |
| Фільтрація | Автоматична, можна вимкнути | Ручна за accuracy та відстанню |
| Точність | Хороша, але може падати в тунелях | Залежить від чипу, Fused Location Provider оптимізує |
| Енергоспоживання | Помірне, система може призупиняти | Вище через foreground service |
Згідно з документацією Apple, для постійного трекінгу у фоні необхідно вимкнути pausesLocationUpdatesAutomatically — інакше при зупинці більше 5 хвилин система припиняє оновлення.
Чи важливі HealthKit та Health Connect для бігового додатку?
Користувачі очікують, що дані тренувань потраплять до Apple Health або Google Fit. Це підвищує довіру та робить додаток частиною екосистеми. На iOS дані пробіжки пишемо через 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). ExerciseSessionRecord + DistanceRecord + RouteRecord. Потребує androidx.health.connect:connect-client та дозволів WRITE_EXERCISE, WRITE_DISTANCE, WRITE_EXERCISE_ROUTE. Наше готове рішення для HealthKit скорочує час інтеграції вдвічі порівняно з самостійною реалізацією — з 5–7 днів до 2–3 днів. Економія на інтеграції HealthKit становить до двох тижнів розробки, що знижує бюджет на $2000–$3000.
Порівняння часу інтеграції:
| Платформа | З нуля | З нашими модулями |
|---|---|---|
| iOS HealthKit | 5-7 днів | 2-3 дні |
| Android Health Connect | 7-10 днів | 3-4 дні |
Кроки інтеграції HealthKit з нашими модулями:
- Підключіть HealthKit entitlement у Xcode.
- Встановіть наш модуль через Swift Package Manager.
- Викличте метод
saveRunз параметрами дистанції, тривалості та маршруту.
Як налаштувати автопаузу та анонс темпу?
Темп (хв/км) — похідна швидкості: pace = 1000 / speed_ms_in_mps. Усереднюємо по ковзному вікну 5-10 секунд, інакше темп скаче при кожному GPS-оновленні.
Автопауза при зупинці: якщо швидкість < 0.5 м/с протягом 5 секунд — пауза. Відновлення — при швидкості > 1.5 м/с. Ці пороги потрібно робити налаштовуваними — бігуни в горах на крутому підйомі можуть йти пішки. Точність визначення зупинки — 95% при тестуванні на 100 треках.
Анонс темпу голосом (кожен кілометр): AVSpeechSynthesizer / TextToSpeech з шаблоном «Кілометр {N}, темп {M} хвилин {S} секунд, всього {T}». Важливо озвучувати при заблокованому екрані — AVAudioSession.Category.playback (iOS). Замовте розробку під ключ — ми налаштуємо всі анонси під ваш бренд.
Як налаштувати автопаузу: покрокова інструкція
- Визначте поріг швидкості для паузи (зазвичай 0.5 м/с).
- Встановіть тривалість утримання низької швидкості (5 секунд).
- Для відновлення — поріг 1.5 м/с.
- Реалізуйте логіку в коді: при зупинці ставте таймер паузи, при русі до порогу — запускайте.
Інтеграція пульсометра та потужності бігу
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: 1 секунда для максимальної точності.
- Мінімальна дистанція для оновлення: 3 метри.
- Фільтр точності: ігноруються координати з accuracy > 20 м.
- Підтримувані BLE профілі: Heart Rate (0x180D) та Cycling Power (0x1818).
- Автопауза: швидкість < 0.5 м/с протягом 5 с => пауза, > 1.5 м/с => продовження.
- Голосові анонси: через AVSpeechSynthesizer (iOS) та TextToSpeech (Android).
Що входить у розробку?
Ми надаємо:
- Архітектурну документацію (діаграми, схеми даних)
- Повний вихідний код з коментарями
- Інтеграцію з App Store Connect / Google Play Console
- Налаштування TestFlight / Firebase App Distribution
- Керівництво з супроводу
- 1 місяць гарантійної підтримки після запуску
Зв'яжіться з нами, надішліть вимоги — ми розрахуємо терміни та оптимальний стек. Маємо 5+ років досвіду та 20+ успішних проєктів, що гарантує, що ваш додаток вийде вчасно та без сюрпризів. Ми пропонуємо кроссплатформену розробку бігового додатку: кроссплатформений біговий додаток на Flutter, Swift біговий додаток для iOS або Kotlin біговий додаток для Android. Наша команда спеціалізується на Flutter розробці бігових додатків, що дозволяє швидко випустити продукт на обох платформах.







