Проєкт трекінгу кур'єра часто починається з простого завдання: показувати клієнту, де знаходиться доставка. Але продакшн вимагає вирішення dead-reckoning при втраті GPS, map snapping на дорожній граф та синхронізацію між трьома компонентами: додатком кур'єра, серверною шиною та додатком клієнта. На одному з проєктів з 500 кур'єрами ми змогли знизити затримку оновлення до 2 секунд, перейшовши з WebSocket на MQTT та налаштувавши Redis з TTL 60 секунд. Такий підхід економить години розробки та знижує експлуатаційні витрати до 30% за рахунок скорочення трафіку. Помилки на етапі вибору протоколу або налаштування батарейного режиму призводять до переписування коду — тому важливо з самого початку закласти правильну архітектуру. Замовте аудит вашого поточного рішення — ми підберемо оптимальну конфігурацію.
Чому трекінг кур'єра — це не просто GPS?
Продакшн-система включає чотири ключові компоненти:
- Dead-reckoning при втраті сигналу GPS — інтерполяція останніх координат з урахуванням швидкості та напрямку.
- Map snapping до доріг — сирі GPS-точки стрибають відносно дороги, їх треба притягувати до дорожнього графу.
- Логіку станів кур'єра: idle → assigned → picking_up → delivering → completed.
- Батарейний компроміс на стороні додатку кур'єра: 5 секунд проти 15 секунд.
Додаток кур'єра: збір та передача координат
На Android використовуємо FusedLocationProviderClient з play-services-location у Foreground Service. Інтервал оновлення — компроміс: 5 секунд дає точність, 15 секунд економить батарею. Для пішого кур'єра — Priority.PRIORITY_HIGH_ACCURACY + 5 секунд. Для авто — Priority.PRIORITY_BALANCED_POWER_ACCURACY + 10 секунд з setMinUpdateDistanceMeters(20f).
На iOS — CLLocationManager з desiredAccuracy: kCLLocationAccuracyBestForNavigation в активному режимі та перемиканням на Significant Location Changes при фоні. activityType = .automotiveNavigation активує фільтрацію шуму GPS.
Фільтрація аномальних точок обов'язкова. GPS на міських вулицях дає стрибки на 50-200 метрів. Простий фільтр: відкидаємо точку, якщо horizontalAccuracy > 50 метрів або розрахункова швидкість > 200 км/год.
func shouldAcceptLocation(_ location: CLLocation) -> Bool { guard location.horizontalAccuracy > 0, location.horizontalAccuracy <= 50 else { return false } if let lastLocation = lastAcceptedLocation { let timeDelta = location.timestamp.timeIntervalSince(lastLocation.timestamp) let distance = location.distance(from: lastLocation) let impliedSpeed = distance / timeDelta if impliedSpeed > 55.6 { return false } } return true } Буферизація в локальній БД + відправка пачками при відновленні мережі — стандартна схема.
Серверна шина: WebSocket чи MQTT?
| Характеристика | WebSocket (Socket.IO) | MQTT (EMQ X) |
|---|---|---|
| Трафік | Вищий через заголовки | Нижчий у 3 рази за рахунок бінарних фреймів |
| Робота з мобільними мережами | Гірше — розрив з'єднання вимагає перепідключення | Краще — QoS + сесії з persist |
| Масштабування | Підходить до кількох тисяч кур'єрів | Легко масштабується до десятків тисяч |
| Стек | Node.js, FastAPI | Mosquitto, EMQ X, AWS IoT Core |
Для невеликих навантажень вибираємо Socket.IO. Для масштабу — MQTT. MQTT визначає бінарний протокол, що дає виграш у трафіку до 70% на мобільній мережі.
Зберігання поточної позиції — Redis: SET courier:{id}:position з TTL 60 секунд. Історія маршруту — TimescaleDB.
Map snapping. Сирі GPS-координати вирівнюємо по дорогах через Google Roads API або OSRM self-hosted (безкоштовно, <10 мс).
Як забезпечити плавну анімацію маркера?
Оновлення кожні 5-10 секунд. Без анімації маркер стрибає. Інтерполюємо рух між двома точками. На Android через ValueAnimator:
val animator = ValueAnimator.ofFloat(0f, 1f).apply { duration = 3000 interpolator = LinearInterpolator() addUpdateListener { animation -> val fraction = animation.animatedFraction val lat = prevLat + (newLat - prevLat) * fraction val lon = prevLon + (newLon - prevLon) * fraction marker.position = LatLng(lat, lon) } } animator.start() На iOS — CADisplayLink або UIView.animate з кастомним timing. Кут повороту маркера обчислюємо через atan2.
У Flutter — TweenAnimationBuilder з Tween<LatLng>. Для google_maps_flutter оновлюємо Marker(position: interpolatedPosition) у Ticker кожні 16 мс.
Стани кур'єра
| Стан | Опис | UI клієнта |
|---|---|---|
| idle | Кур'єр вільний | "Шукаємо кур'єра" |
| assigned | Призначений на замовлення | "Кур'єр їде до ресторану" |
| picking_up | Забирає замовлення | "Кур'єр біля ресторану" |
| delivering | Веде замовлення | "Кур'єр везе ваше замовлення" |
| completed | Доставлено | "Замовлення доставлено" |
Переходи керуються сервером. Замовте розробку під ваш сценарій — ми врахуємо всі нюанси.
Що входить у роботу
- Проектування протоколу (WebSocket/MQTT) та схеми даних.
- Розробка додатку кур'єра з фоновим трекінгом.
- Реалізація серверної шини з Redis та TimescaleDB.
- Розробка UI клієнта з плавною анімацією та станами.
- Навантажувальне тестування: 100/500/1000 кур'єрів.
- Документація та навчальна зустріч.
Процес роботи та терміни
- Аналітика: аудит поточної інфраструктури, визначення протоколу.
- Проектування: архітектура шини, схеми станів.
- Розробка: реалізація додатків та сервера.
- Тестування: юніт-тести, інтеграційне, навантажувальне.
- Деплой: реліз в App Store та Google Play.
Термін: від 2 до 4 тижнів залежно від кількості платформ та готовності серверного API.
Чому варто працювати з нами?
Ми реалізували трекінг для 15+ логістичних проєктів. Наші інженери сертифіковані з iOS та Android. Гарантуємо точність треку до 5 метрів та стабільність при 10 000 одночасних сесій. Отримайте консультацію щодо вашого кейсу — зв'яжіться з нами.







