Розроблення відслідковування статусу заказу в мобільному додатку
Користувач відкриває додаток через 40 хвилин після оформлення заказу й бачить статус «В обробці» — той же, що був одразу після оплати. Телефонує в підтримку. Проблема не в логістиці: кур'єр уже їде, координати оновлюються на сервері кожні 30 секунд. Проблема в тому, що мобільний додаток не підключен до цього потоку.
Таймлайн статусів і карта кур'єра — це два різних механізми з різними вимогами до оновлення, й змішувати їх в одному polling-запиті — перша архітектурна помилка.
Таймлайн статусів: WebSocket проти polling
Статус заказу змінюється рідко — 5-7 разів за весь життєвий цикл. Для цього підходить long-polling або SSE (Server-Sent Events): з'єднання відкрито, сервер пушить подію тільки при зміні статусу. WebSocket тут надмірна, хоча часто вибирається за інерцією.
На iOS реалізація через URLSession з SSE виглядає так:
let request = URLRequest(url: URL(string: "https://api.example.com/orders/\(orderId)/status-stream")!)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// парсимо text/event-stream побудочно
}
task.resume()
Краще використовувати готову бібліотеку — IVALiveEventSource або Swift Package swift-eventsource від LaunchDarkly. Вони коректно обробляють reconnect і heartbeat.
На Android — OkHttp з EventSource з бібліотеки com.launchdarkly:okhttp-eventsource. Писати нативний HttpURLConnection для SSE вручну — трата часу на обробку краєвих випадків.
Структура таймлайну на екрані
Для відображення прогресу статусів використовують RecyclerView (Android) або UICollectionView з кастомним layout (iOS). Типова помилка — зберігати «пройдені» статуси тільки на клієнті. Якщо користувач видалив й переустановив додаток, історія пропадає. Усі пройдені статуси з часовими мітками повинні повертатися з сервера в масиві:
{
"currentStatus": "courier_assigned",
"timeline": [
{ "status": "created", "timestamp": "2024-03-15T10:00:00Z" },
{ "status": "confirmed", "timestamp": "2024-03-15T10:02:30Z" },
{ "status": "courier_assigned", "timestamp": "2024-03-15T10:15:00Z" }
]
}
Карта кур'єра: окремий потік даних
Координати кур'єра оновлюються кожні 15-30 секунд — це вже не SSE, а WebSocket або окремий polling з коротким інтервалом. Змішувати його з таймлайном статусів в одному endpoint-е означає або перегружати статусний потік, або оновлювати карту занадто рідко.
На практиці робимо так:
- Статуси — SSE або push-сповіщення (Firebase Cloud Messaging)
- Координати — WebSocket з інтервалом 15-30 секунд або polling
/orders/{id}/courier-location
Сглаживання маркера кур'єра
Маркер кур'єра на карті прискочує, якщо просто встановити нові координати напрямку. Правильно — анімувати переміщення між точками. На Android через ValueAnimator:
val animator = ValueAnimator.ofFloat(0f, 1f).apply {
duration = 1000
addUpdateListener { animation ->
val fraction = animation.animatedValue as Float
val lat = startLat + (endLat - startLat) * fraction
val lng = startLng + (endLng - startLng) * fraction
courierMarker.position = LatLng(lat, lng)
}
}
animator.start()
На iOS через CADisplayLink або UIView.animate з проміжними координатами.
Обертання іконки кур'єра по напрямку руху обраховується через atan2(deltaLat, deltaLng) — не забудьте перевести радіани в градуси для marker.rotation.
Push-сповіщення як резервний канал
Користувач мінімізує додаток — WebSocket й SSE розриваються. Ключові смени статусу (кур'єр забрав заказ, кур'єр рядом, заказ доставлен) дублюються через FCM/APNs. На iOS потрібен UNUserNotificationCenter, на Android — FirebaseMessagingService.
Тонкість: сповіщення «кур'єр рядом» втрачає сенс, якщо прийшло через 10 хвилин після доставки. Сервер повинен перевіряти актуальність події перед відправкою пуша — це серверна логіка, не мобільна.
Що входить у роботу
- Таймлайн статусів з SSE або FCM-пушами
- Карта кур'єра з анімованим маркером (Google Maps SDK або MapKit)
- WebSocket або polling для координат з правильним lifecycle (onPause/onResume / viewDidDisappear)
- Обробка оффлайн-стану: черга подій й синхронізація при відновленні мережі
Терміни
3–5 днів для повного flow: таймлайн + карта + пуші. Тільки таймлайн без карти — 1–2 дні. Вартість розраховується індивідуально після аналізу вимог.







