Содержание
Офлайн-режим нужен приложениям, которыми пользуются там, где связь плохая или её нет: курьерам, выездным сотрудникам, складским работникам, путешественникам. Для большинства приложений достаточно кешировать данные для просмотра и аккуратно сообщать об отсутствии сети. Полноценный подход offline-first — когда пользователь создаёт и меняет данные без интернета, а приложение потом их синхронизирует — заметно сложнее и требует продуманной синхронизации и разрешения конфликтов.
Разберём уровни поддержки офлайна, хранение данных на устройстве, синхронизацию и конфликты.
Когда приложению нужен офлайн-режим?
Офлайн-режим нужен, когда работа пользователя не должна останавливаться из-за связи. Если без сети человек не может выполнить свою задачу, а сеть у него регулярно пропадает, офлайн-режим становится ключевой функцией, а не приятным дополнением.
- выездные сотрудники — монтажники, инспекторы, торговые представители;
- логистика — курьеры, водители, сборщики на складе с плохим покрытием;
- медицина и полевые исследования — заполнение форм там, где нет связи;
- путешествия — билеты, карты, бронирования;
- контент — книги, курсы, подкасты для просмотра в дороге.
Про выбор между приложением и сайтом — статьи нужно ли мобильное приложение и PWA вместо мобильного приложения.
Какие бывают уровни поддержки офлайна?
Поддержка офлайна бывает разной глубины. Чем больше пользователь может сделать без сети, тем сложнее разработка и тестирование.
| Уровень | Что может пользователь без сети | Сложность |
|---|---|---|
| Сообщение об ошибке | Видит понятное сообщение и кнопку повтора | Минимальная |
| Кеш для просмотра | Смотрит ранее загруженные данные | Низкая |
| Отложенные действия | Создаёт записи, которые отправятся позже | Средняя |
| Offline-first | Полноценно работает, синхронизация в фоне | Высокая |
Начинать стоит с честного вопроса: какие сценарии пользователи действительно выполняют без сети, — и делать офлайн только для них.
Фото: Unsplash
Как хранить данные на устройстве?
Для офлайн-режима данные хранят в локальной базе данных на устройстве, а интерфейс читает их оттуда, а не напрямую из сети. Сеть становится способом синхронизации, а не источником данных для каждого экрана.
- Локальная база — SQLite или надстройки над ней, которые есть для нативных и кроссплатформенных приложений.
- Только нужное — на устройство загружаются данные пользователя и его задач, а не вся база компании.
- Метки времени и версии — у каждой записи есть версия или время изменения для синхронизации.
- Шифрование — локальная база шифруется, токены хранятся в защищённом хранилище системы.
- Очистка — при выходе из аккаунта данные удаляются.
Как синхронизировать изменения?
Изменения, сделанные без сети, записываются в локальную очередь операций. Когда связь появляется, приложение отправляет операции на сервер по порядку и получает изменения, сделанные другими. Важно, чтобы повторная отправка одной и той же операции не создавала дубли.
type PendingOperation = {
id: string // уникальный ключ — сервер не создаст дубль при повторе
entity: 'delivery'
entityId: string
action: 'update_status'
payload: { status: 'delivered'; signedAt: string; photoId?: string }
createdAt: string
attempts: number
}
Правила синхронизации:
- каждая операция имеет уникальный ключ идемпотентности — подробнее в статье идемпотентность платежей и вебхуков;
- синхронизация запускается при появлении сети и в фоне, с повторами;
- с сервера загружаются только изменения с момента последней синхронизации;
- большие файлы — фотографии, подписи — загружаются отдельно и возобновляются после обрыва;
- пользователь видит статус: что отправлено, а что ждёт сети.
Как разрешать конфликты?
Конфликт возникает, когда одну и ту же запись изменили на устройстве без сети и на сервере. Стратегию разрешения выбирают для каждого типа данных заранее — универсального правила нет.
- последнее изменение побеждает — просто, подходит для некритичных полей;
- слияние по полям — разные поля из разных источников объединяются;
- приоритет источника — например, статус от диспетчера важнее статуса из приложения;
- ручное разрешение — пользователь видит обе версии и выбирает;
- только добавление — для журналов и комментариев конфликтов нет вовсе.
Как тестировать офлайн-режим?
Офлайн-режим ломается в ситуациях, которых нет в офисе с хорошим Wi-Fi: медленная сеть, обрыв посреди отправки, приложение закрыто до синхронизации. Поэтому эти сценарии тестируют специально.
- эмуляция медленной и нестабильной сети;
- обрыв связи во время отправки и загрузки файлов;
- закрытие приложения с неотправленными изменениями;
- одновременное изменение одной записи на двух устройствах;
- долгая работа без сети и синхронизация большого объёма.
О тестировании мобильных и веб-продуктов — статьи ручное тестирование в небольшой команде и Flutter, React Native или натив.
Коротко
- Офлайн-режим нужен, когда работа пользователя не должна зависеть от связи.
- Уровни: сообщение об ошибке, кеш для просмотра, отложенные действия, offline-first.
- Интерфейс читает данные из локальной базы, а сеть служит для синхронизации.
- Изменения копятся в очереди операций с ключами идемпотентности.
- Стратегию разрешения конфликтов выбирают для каждого типа данных заранее.
Частые вопросы
Можно ли сделать офлайн-режим в веб-приложении?
Да, через Service Worker и хранилище браузера IndexedDB. Возможности скромнее, чем в нативном приложении, но для просмотра данных и заполнения форм без сети этого обычно хватает.
Насколько офлайн-режим увеличивает стоимость разработки?
Кеш для просмотра добавляет немного. Полноценный offline-first с изменением данных, синхронизацией и конфликтами может увеличить объём работы над соответствующими функциями в полтора-два раза.
Как защитить данные, хранящиеся на устройстве?
Хранить на устройстве только необходимое, шифровать локальную базу, хранить токены в защищённом хранилище системы и очищать данные при выходе из аккаунта.



