Два пользователя одновременно редактируют список задач: один на iPhone, другой на Android. Данные быстро расходятся, конфликты накапливаются, и итоговое состояние непредсказуемо. REST API не обеспечивает real-time, а создание собственного WebSocket-сервера с CRDT требует месяцев разработки и отладки. Потери данных из-за конфликтов могут достигать 15% от всех изменений.
Мы используем Liveblocks — управляемую инфраструктуру для совместного редактирования. Она предоставляет готовый WebSocket-сервер, CRDT-хранилище (на базе Liveblocks Storage), presence, комментарии и нотификации через облачное API. В браузере интеграция занимает один день, в мобильном приложении на React Native — от недели. Это ускоряет выход на рынок в 3–6 раз по сравнению с самостоятельной реализацией. Экономия на инфраструктуре может быть значительной.
Что даёт Liveblocks из коробки?
- Storage — CRDT-хранилище на базе собственной реализации с
LiveObject,LiveList,LiveMap. Изменения реплицируются между клиентами за <100 мс в одном регионе. - Presence — эфемерный стейт пользователя: позиция курсора, выделение, статус. Не персистируется, только онлайн.
- Yjs-интеграция — через
@liveblocks/yjsможно использовать Yjs документы совместно с веб-редакторами (Tiptap, Slate).
Согласно Wikipedia, CRDT гарантирует консистентность в распределённых системах.
Как подключить Liveblocks в React Native?
- Установите пакеты:
npm install @liveblocks/client @liveblocks/react - Создайте клиент с помощью
createClientи настройте аутентификацию. - Определите типы Presence и Storage для вашего приложения.
- Экспортируйте хуки через
createRoomContext. - Оберните экран редактора в
RoomProvider.
import { createClient } from '@liveblocks/client'; import { createRoomContext } from '@liveblocks/react'; const client = createClient({ authEndpoint: '/api/liveblocks-auth', // или publicApiKey для прототипов }); type Presence = { cursor: { x: number; y: number } | null; selectedItemId: string | null; }; type Storage = { items: LiveList<{ id: string; text: string; done: boolean }>; }; export const { RoomProvider, useMyPresence, useStorage, useMutation } = createRoomContext<Presence, Storage>(client); RoomProvider оборачивает экран-редактор. Внутри useStorage даёт иммутабельный снапшот, useMutation — транзакционные мутации.
Проблема в RN: @liveblocks/client использует fetch и WebSocket, которые есть, но EventSource (SSE для нотификаций) отсутствует без полифила. Установите react-native-event-source и глобальный полифил:
import EventSource from 'react-native-event-source'; global.EventSource = EventSource; Получите консультацию по настройке клиента и полифилов — это сэкономит вам до 40% времени на интеграцию.
Что такое Presence в мобильном контексте?
В браузере presence — координаты мыши. В мобильном приложении — другие кейсы:
- Совместное редактирование списка задач: presence =
{ focusedItemId: string | null }— подсвечиваем редактируемый элемент. - Совместная доска: presence =
{ x, y, tool: 'pen'|'eraser' }— показываем стилус. - Документный редактор: presence =
{ selection: { anchor, focus } | null }— выделение текста.
Используйте useOthersConnectionIds и useOther для реактивных данных о присутствующих. Для оптимизации — useOthersMapped выбирает только нужные поля.
Почему стоит выбрать Liveblocks для мобильного приложения?
| Критерий | Liveblocks | Самостоятельная реализация |
|---|---|---|
| Время до продакшена | 2–8 недель | 3–6 месяцев |
| Поддержка офлайна | Требует доработки | Полный контроль |
| Стоимость инфраструктуры | Подписка | Значительные затраты |
| SLA | 99.9% (Pro) | Зависит от вас |
| Data residency | Только облако | Полный контроль |
Сравнение показывает: Liveblocks выигрывает по скорости интеграции, но уступает в офлайн-режиме и контроле данных. Для большинства MVP и средних проектов этого достаточно.
Офлайн-режим: чего нет из коробки
Liveblocks не предоставляет офлайн-персистентность. При отсутствии сети изменения теряются. Мы решаем это буфером мутаций в AsyncStorage:
Пример реализации офлайн-буфера
const [pendingMutations, setPendingMutations] = useAtom(pendingMutationsAtom); NetInfo.addEventListener(state => { if (!state.isConnected) { const snapshot = storage.toObject(); AsyncStorage.setItem('offline_snapshot', JSON.stringify(snapshot)); } else { pendingMutations.forEach(mutation => mutation()); setPendingMutations([]); } }); Это не настоящий CRDT-офлайн — merge не гарантирован при одновременных изменениях офлайн. Для полного решения комбинируем с Yjs через @liveblocks/yjs и кастомным локальным провайдером. Сложнее, но правильнее.
| Стратегия офлайна | Сложность | Гарантия консистентности | Время внедрения |
|---|---|---|---|
| Буфер мутаций | Низкая | Нет, возможна потеря | 1–2 недели |
| Локальная копия + merge | Средняя | Частичная (last-write-wins) | 2–4 недели |
| Yjs с локальным провайдером | Высокая | Полная CRDT | 4–8 недель |
AppState и реконнект
iOS убивает WebSocket при фоне. SDK переподключается автоматически, но room.getStatus() проходит через reconnecting. Если UI не реагирует, пользователь видит устаревший стейт. Обрабатывайте явно:
const status = useStatus(); if (status === 'reconnecting') { return <ReconnectingBanner />; } Что входит в работу
- Настройка клиента Liveblocks и провайдера аутентификации.
- Интеграция Storage и Presence для вашего use-case.
- Реализация офлайн-буфера (опционально).
- Yjs-интеграция для совместимости с веб-редакторами.
- Тестирование реконнекта и обработки appState.
- Документация и обучение команды.
- Поддержка 2 недели после деплоя.
Закажите демо-проект с базовой интеграцией — мы покажем, как Liveblocks работает в вашем контексте. Свяжитесь с нами для оценки проекта за 1–2 рабочих дня.
Как мы оцениваем проект
Ориентировочные сроки:
- Базовая интеграция (Storage + Presence, без офлайна): 2–4 недели.
- С офлайн-буфером: 3–6 недель.
- С Yjs и полным офлайном: 6–10 недель.
Стоимость рассчитывается индивидуально после анализа вашего MVP. Получите консультацию — оценим проект за 1–2 рабочих дня.
Наш опыт: более 5 лет в мобильной разработке, 15+ проектов с real-time синхронизацией. Работаем с Liveblocks с момента его публичного релиза.







