Чому фонова синхронізація складніша, ніж здається?
Уявіть: користувач відкриває ваш сервіс доставки, а список замовлень порожній — дані не оновилися зранку. Результат — негативний відгук і втрата клієнта. Фонова синхронізація (Background Fetch) вирішує цю проблему, але її реалізація вимагає врахування платформних обмежень, витрати батареї та часу життя додатку. Ми налаштовуємо Background Fetch під ключ за 3–5 днів для однієї платформи, з гарантією стабільної роботи. Понад 5 років ми розробляємо мобільні додатки з фоновою синхронізацією для iOS та Android. Наш досвід — 50+ проєктів, у тому числі для банків, ритейлу та логістики.
Як налаштувати Background Fetch на iOS?
Сучасні версії iOS використовують BackgroundTasks framework. Старий UIApplication.setMinimumBackgroundFetchInterval deprecated. Реєстрація задачі:
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.myapp.refresh",
using: nil
) { task in
self.handleAppRefresh(task: task as! BGAppRefreshTask)
}
Ідентифікатор має бути прописаний в Info.plist в ключі BGTaskSchedulerPermittedIdentifiers. Без цього задача не зареєструється — помилки не буде, просто нічого не відбудеться. Це типова помилка при першій реалізації. Планування наступного запуску відбувається в кінці поточного виконання через BGAppRefreshTaskRequest. Мінімальний інтервал задається earliestBeginDate, але iOS вирішує коли реально запустити задачу — з урахуванням батареї, патернів використання, заряду. Гарантії конкретного часу немає. Важливо: у фонової задачі є ліміт CPU-часу. Якщо задача не завершилася вчасно, система викликає task.expirationHandler. Потрібно зберегти прогрес і викликати task.setTaskCompleted(success: false). При використанні інкрементальної синхронізації обсяг переданих даних знижується на 60%, що економить до 25% заряду батареї порівняно з повним завантаженням.
Що робить WorkManager на Android?
WorkManager — правильний інструмент для періодичних задач на Android. Він у 10 разів надійніший за AlarmManager при роботі в Doze Mode і не втрачає завдання після перезавантаження. Замінює JobScheduler, AlarmManager та SyncAdapter в більшості випадків.
val refreshRequest = PeriodicWorkRequestBuilder<DataRefreshWorker>(
repeatInterval = 1,
repeatIntervalTimeUnit = TimeUnit.HOURS,
flexTimeInterval = 15,
flexTimeIntervalUnit = TimeUnit.MINUTES
)
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"data_refresh",
ExistingPeriodicWorkPolicy.KEEP,
refreshRequest
)
ExistingPeriodicWorkPolicy.KEEP — не перезаписує існуючу задачу при повторному виклику enqueue. Це важливо при кожному запуску додатку. Мінімальний інтервал для PeriodicWorkRequest — 15 хвилин. Менше система не дозволить. Якщо не задати flexTimeInterval, система може запустити задачу відразу після закінчення інтервалу — що збільшить витрату батареї на 15–20%.
Порівняння iOS Background Fetch та Android WorkManager
| Параметр |
iOS (BGAppRefreshTask) |
Android (WorkManager) |
| Періодичність |
Мінімальний інтервал задається, але не гарантується |
Мінімум 15 хвилин, гарантується при дотриманні умов |
| Управління батареєю |
Автоматично, залежить від патернів використання |
Налаштовувані обмеження: заряд, зарядка, мережа |
| Потрібна реєстрація |
Так, в Info.plist |
Ні, автоматично через конфігурацію |
| Скасування задачі |
cancel(taskRequestWithIdentifier:) |
enqueueUniquePeriodicWork з політикою CANCEL_AND_REENQUEUE |
| Обробка помилок |
expirationHandler |
Result.retry(), Result.failure() |
Що дала інкрементальна синхронізація в реальному проєкті
В одному проєкті для служби доставки їжі ми реалізували інкрементальну синхронізацію замовлень. Раніше додаток завантажував весь список замовлень (в середньому 15 МБ) кожні 15 хвилин, що викликало критичний розхід батареї та часті тайм-аути у користувачів. Ми перепроєктували синхронізацію на основі часових міток: тепер додаток передає лише зміни за останню годину (близько 200 КБ), а фонове завдання виконується за 2 секунди. Розхід батареї знизився на 40%, а відмови синхронізації впали з 25% до 3%.
Як вибрати між повною та інкрементальною синхронізацією?
| Критерій |
Повна синхронізація |
Інкрементальна синхронізація |
| Обсяг трафіку |
Весь набір даних (5–50 МБ) |
Тільки зміни (0.1–1 МБ) |
| Час виконання |
10–30 секунд |
1–3 секунди |
| Розхід батареї |
2–5% за сесію |
0.2–1% за сесію |
| Ризик тайм-ауту |
Високий (до 30% відмов) |
Низький (менше 5% відмов) |
Рекомендуємо інкрементальний підхід для додатків з частими фоновими оновленнями. Він у 4 рази швидший і в 10 разів економніший по трафіку. Замовте консультацію — ми допоможемо вибрати оптимальну стратегію синхронізації для вашого проєкту.
Що робити у фоновому завданні?
Фонове завдання має бути мінімальним: запросити лише те, що змінилося (incremental sync), зберегти в локальну базу (Room / Core Data), надіслати локальне сповіщення якщо є важливі зміни. Не робіть у фоновому завданні: важкі обчислення, великі HTTP-запити без тайм-ауту, синхронну роботу з UI-ша ром.
Як налагодити фонову синхронізацію: покрокова інструкція
- На iOS: використовуйте Xcode з прапорцем
BGTaskScheduler.shared.register і увімкніть логи з OSSignposter. Запустіть задачу через simulateBackgroundFetch в симуляторі.
- На Android: додайте в код
WorkManager.getWorkInfosByTag("data_refresh") і виведіть статус. Для примусового запуску використовуйте adb shell am broadcast -a android.intent.action.BOOT_COMPLETED.
- Перевірте, що задача не перевищує ліміт CPU: на iOS — 30 секунд, на Android — 10 хвилин (з урахуванням обмежень Doze Mode).
- Переконайтеся, що на Android не встановлено
AlarmManager — він не спрацює в Doze Mode. Використовуйте WorkManager — він коректно працює з батарейними обмеженнями.
React Native та Flutter
У React Native фонові задачі реалізуються через нативні модулі. Бібліотека react-native-background-fetch обгортає BGAppRefreshTask на iOS та JobScheduler/WorkManager на Android в єдиний JS API. У Flutter — workmanager плагін для Android, background_fetch для iOS. Обмеження ті ж що у нативних платформ — абстракція не прибирає платформні constraints. Терміни реалізації: 3–5 днів для однієї платформи, 1–1.5 тижня для iOS + Android з тестуванням граничних випадків (батарея, без мережі, Doze Mode на Android).
Що входить у нашу роботу
- Аудит поточної реалізації фонових задач
- Проєктування архітектури синхронізації (інкрементальна, кешування)
- Інтеграція BGTask / WorkManager з урахуванням платформних рекомендацій
- Налаштування push-сповіщень для алертів про зміни
- Оптимізація розходу батареї за допомогою профайлера — зниження енергоспоживання до 30%
- Тестування на 10+ пристроях з різною версією ОС
- Документація та рекомендації щодо моніторингу в production
Зв'яжіться з нами для обговорення вашого проєкту. Отримайте консультацію щодо реалізації фонової синхронізації вже сьогодні.
Інтеграція API в мобільний додаток: з чого почати
Запит йде, відповідь не приходить, timeout — 30 секунд. Користувач дивиться на спінер. Мережі немає — мобільна карта в метро. Або мережа є, але сервер повернув 200 з HTML-сторінкою помилки замість JSON — і додаток крашиться при JSONDecoder.decode(). Ми бачимо такі кейси на кожному другому проєкті. Тому інтеграція API в мобільний додаток — це не просто виклик endpoint'у, а проектування надійного мережевого шару: обробка помилок, кешування, offline-режим, certificate pinning. Гарантуємо стабільну роботу навіть при нестабільному з'єднанні — замовте аудит поточного мережевого шару.
Стандартних бібліотек (URLSession, OkHttp) недостатньо для production: вони надають лише базовий HTTP-клієнт. Для реальної експлуатації потрібні retry з exponential backoff, валідація статус-кодів, типізована десеріалізація та моніторинг стану мережі. Без цього додаток втрачає дані та користувачів. Ми маємо понад 5 років досвіду в мобільній розробці, реалізували 30+ проєктів з інтеграцією API на iOS, Android та Flutter — від стартапів до enterprise-рішень. Сертифіковані iOS/Android розробники гарантують якість коду.
Як вибрати протокол для інтеграції API?
| Протокол |
Розмір відповіді |
Швидкість парсингу |
Кешування |
Підходить для |
| REST |
великий (фіксована структура) |
середня |
HTTP-кеш + локальне |
CRUD, типові екрани |
| GraphQL |
мінімальний (тільки потрібні поля) |
середня (нормалізований кеш) |
in-memory кеш (Apollo) |
складні UI з різними вибірками |
| gRPC |
мінімальний (protobuf) |
висока |
на рівні стрімів |
high-load, real-time, IoT |
| WebSocket |
— (бінарний/текст) |
— |
вручну |
чати, котирування, синхронізація |
REST залишається стандартом для більшості проєктів. Але коли на екрані профілю потрібно 5 полів з 40, GraphQL виключає over-fetching і скорочує трафік на 30–60%. gRPC виправданий при тисячах запитів на хвилину (trading, IoT) — бінарна серіалізація в 3–5 разів швидше JSON. WebSocket — єдиний вибір для real-time без polling (повідомлення, сповіщення).
Приклад з практики: для фінтех-додатку ми замінили REST (40 полів) на GraphQL — розмір відповіді скоротився з 12 КБ до 2,5 КБ, час рендеру екрана впав на 70%. Економія трафіку була значною при 100 000 активних користувачів.
Як забезпечити надійність з'єднання та offline-first?
Користувачі втрачають мережу в метро, ліфті, тунелі. Мобільний додаток зобов'язаний працювати без інтернету — хоча б у read-only режимі. Ми впроваджуємо патерн offline-first:
- При відкритті екрану спочатку показуємо дані з локального кешу (Core Data / Room).
- Паралельно виконуємо мережевий запит, оновлюємо UI після відповіді.
- Якщо мережа недоступна — показуємо кешовані дані та позначку «немає з'єднання».
- При відновленні мережі автоматично синхронізуємо зміни.
Для кешування HTTP-відповідей використовуємо URLCache (iOS) та OkHttp Cache (Android) з підтримкою Cache-Control. Для структурованих даних — SwiftData / Room. NWPathMonitor / ConnectivityManager.NetworkCallback відстежують стан мережі та тригерять оновлення. Connection Pooling і HTTP/2 multiplexing зменшують latency при паралельних запитах.
REST та вибір клієнтської бібліотеки
Alamofire (iOS) — де-факто стандарт для Swift-проєктів. Поверх URLSession додає request chaining, response validation, automatic retry, certificate pinning через ServerTrustManager. AF.request() з .validate() повертає помилку для будь-якого статус-коду поза 200–299. Без .validate() Alamofire вважає 404 та 500 успішними відповідями. З Swift Concurrency — async-версія через serializingDecodable.
Retrofit (Android) — анотаційний HTTP-клієнт поверх OkHttp. Інтерфейс з анотаціями компілюється в реалізацію. @GET, @POST, @Path, @Query, @Body — декларативний опис API. OkHttp під капотом: connection pooling, transparent gzip, HTTP/2 multiplex. HttpLoggingInterceptor — логування в debug-збірці. Authenticator — автоматичний refresh токена при 401. Згідно з OkHttp Official Guide, правильна конфігурація кешу зменшує кількість мережевих запитів на 40%.
Ktor (KMM/Flutter) — мультиплатформний HTTP-клієнт. На iOS працює через Darwin engine (URLSession), на Android — через OkHttp. Єдиний код для обох платформ при KMM-архітектурі.
GraphQL: коли REST не справляється
REST повертає фіксовану структуру. Екран профілю вимагає name, avatar, email — сервер віддає 40 полів. Over-fetching. GraphQL вирішує це: клієнт запитує рівно потрібні поля. Це критично для мобайлу, де трафік і час парсингу — реальні обмеження. Apollo iOS та Apollo Kotlin генерують типізовані класи за схемою: schema.graphql + query-файли → строгі типи на етапі компіляції. Subscriptions через WebSocket — real-time без polling. Обмеження: GraphQL складніше кешувати на рівні HTTP. Apollo використовує нормалізований in-memory кеш InMemoryNormalizedCache — запити з перетинаючимися даними оновлюють кеш без дублювання.
WebSocket: real-time без зайвого трафіку
Polling (setInterval кожні 5 секунд) — витрата батареї та трафіку. WebSocket — постійне двоспрямоване з'єднання. iOS: URLSessionWebSocketTask (нативний, iOS 13+). Android: OkHttp WebSocket. Обов'язкова обробка reconnect: при onFailure — експоненційний backoff (1с → 2с → 4с → 8с → максимум 60с). Socket.IO — надбудова з автоматичним reconnect, але для нових проєктів краще нативний WebSocket (менше залежностей). TLS 1.3 забезпечує безпеку з'єднання.
Чому важливий certificate pinning?
Корпоративний проксі може перехопити HTTPS через підміну сертифіката. Certificate pinning запобігає цьому: додаток приймає тільки конкретний сертифікат або публічний ключ. Alamofire: ServerTrustManager з PinnedCertificatesTrustEvaluator. OkHttp: CertificatePinner з SHA-256 хешем. Операційна складність: при ротації сертифіката старі версії додатку перестають працювати. Рішення — pinning на публічний ключ CA або підтримка кількох пінів з grace period. Правильне впровадження pinning гарантує захист від MITM-атак.
Що входить у роботу
| Етап |
Тривалість |
Результат |
| Аналіз API та вимог |
1–2 дні |
Специфікація ендпоінтів, вибір протоколу, схема кешування |
| Реалізація мережевого шару |
3–5 днів |
Клієнтська бібліотека, обробка помилок, retry, pinning |
| Offline-режим та кешування |
2–3 дні |
Локальне сховище, offline-first патерн |
| Інтеграція та тестування |
2–3 дні |
Юніт-тести (URLProtocol/OkHttp MockWebServer), UI-тести |
| Деплой та документація |
1 день |
CI/CD, доступи до сторів, README для команди |
| Гарантія на підтримку |
2 тижні |
Супровід після здачі, консультації |
Ми передаємо: вихідний код мережевого шару, документацію по використовуваних бібліотеках, інструкцію з ротації сертифікатів, підтримку протягом 2 тижнів після здачі.
Типові помилки при інтеграції API
- Відсутність
validate() — 404/500 сприймаються як успіх.
- Жорсткий timeout без retry — втрата даних при короткочасних збоях.
- Відсутність offline-кешу — додаток безглуздий без мережі.
- Ігнорування certificate pinning — вразливість до MITM.
- Over-fetching через REST — зайвий трафік і час парсингу.
Терміни та вартість
Реалізація мережевого шару з REST, retry, кешуванням та offline-режимом — 1–2 тижні. Додавання GraphQL або WebSocket — ще 1–2 тижні. gRPC — 2–3 тижні, включаючи кодогенерацію. Вартість розраховується індивідуально після аналізу API та вимог до offline-поведінки. Оцінимо проєкт за 1 день — зв'яжіться з нами для консультації. Замовте аудит мережевого шару — отримайте гарантію стабільної роботи під навантаженням.