Интеграция Yjs для real-time collaboration в мобильном приложении

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.
Разработка и поддержка любых видов мобильных приложений:
Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Предлагаемые услуги
Показано 1 из 1 услугВсе 1735 услуг
Интеграция Yjs для real-time collaboration в мобильном приложении
Сложная
от 1 недели до 3 месяцев
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1054
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    864
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    445

Интеграция Yjs для real-time collaboration в мобильном приложении

Y.js — CRDT-библиотека на JavaScript, которую всё чаще тащат в React Native-проекты, рассчитывая получить Google Docs-опыт в мобильном приложении. Реальность сложнее: Y.js проектировался под браузерное окружение, у него нет официального Flutter SDK, а Hermes на старых RN-версиях встречает WASM-бинарник @automerge/automerge с паникой при инициализации. Разберём, где настоящие грабли.

Как устроена синхронизация в Y.js

Каждый Y.Doc содержит внутренний стейт-вектор — Map<clientId, maxClock>. При подключении двух клиентов они обмениваются своими стейт-векторами и запрашивают только дельту: Y.encodeStateAsUpdateV2(doc, remoteStateVector). Это дифференциальный протокол — при реконнекте не нужно передавать весь документ.

Транспортный уровень реализован через провайдеры:

Провайдер Транспорт Особенности
y-websocket WebSocket Официальный, есть серверная часть
y-webrtc WebRTC DataChannel P2P, нет в RN без полифила
y-indexeddb IndexedDB Только браузер
Кастомный SQLite / AsyncStorage Нужна ручная реализация для RN

Для React Native: y-websocket на транспортном уровне работает через react-native-get-random-values + нативный WebSocket. Персистентность — кастомный провайдер поверх react-native-sqlite-storage или op-sqlite.

Персистентность через SQLite в React Native

Готового y-sqlite-провайдера для RN нет. Минимальная реализация:

import * as Y from 'yjs';
import { openDatabase } from 'react-native-sqlite-storage';

const db = openDatabase({ name: 'collab.db' });

// Инициализация таблицы
db.transaction(tx => {
  tx.executeSql(
    'CREATE TABLE IF NOT EXISTS ydocs (id TEXT PRIMARY KEY, update BLOB, ts INTEGER)'
  );
});

// Сохранение при каждом изменении
ydoc.on('updateV2', (update: Uint8Array, origin: unknown) => {
  if (origin === 'sqlite-load') return; // не зацикливаемся
  const encoded = Buffer.from(update).toString('base64');
  db.transaction(tx => {
    tx.executeSql(
      'INSERT OR REPLACE INTO ydocs (id, update, ts) VALUES (?, ?, ?)',
      [docId, encoded, Date.now()]
    );
  });
});

// Загрузка при открытии
db.transaction(tx => {
  tx.executeSql('SELECT update FROM ydocs WHERE id = ?', [docId], (_, result) => {
    if (result.rows.length > 0) {
      const raw = Buffer.from(result.rows.item(0).update, 'base64');
      Y.applyUpdateV2(ydoc, new Uint8Array(raw), 'sqlite-load');
    }
  });
});

Проблема с этим подходом: при частом редактировании (типпинг в реальном времени) updateV2 триггерится при каждом символе. Батчинг обязателен — debounce на 300–500 мс или накопление через Y.mergeUpdatesV2. Без этого в продакшене у вас быстро закончится место на устройстве, а транзакции в SQLite начнут блокировать JS-тред.

Серверная часть: y-websocket vs собственный сервер

Официальный y-websocket сервер минималистичен — хранит документы в памяти. Для продакшена нужно:

  1. Персистентность — сохранение Y.encodeStateAsUpdateV2() при отключении последнего клиента. Подходит LevelDB (пакет y-leveldb) или PostgreSQL с BYTEA-колонкой.
  2. Авторизацияy-websocket не проверяет токены. Нужен middleware, перехватывающий Upgrade-запрос и проверяющий JWT до апгрейда соединения.
  3. Масштабирование — один процесс y-websocket не знает о других. При горизонтальном масштабировании — Redis PubSub как шина между узлами.

Альтернатива: Hocuspocus (надстройка над y-websocket с авторизацией, хуками и готовой персистентностью). Для большинства проектов Hocuspocus закрывает 90% серверных потребностей без написания custom-сервера.

Типичные ошибки при интеграции в React Native

clientID Y.js генерируется случайно при создании Y.Doc. Если создавать новый Y.Doc при каждом маунте компонента — клиент будет иметь новый ID после каждого размонтирования, и стейт-вектор сервера будет накапливать мёртвые записи. Фикс: хранить ydoc в ref или глобальном стейте, не пересоздавать.

Awareness (курсоры, онлайн-статус) через y-protocols/awareness требует активного WebSocket. При переходе приложения в фоновый режим на iOS WebSocket может быть убит через 30–60 секунд. awareness.setLocalState(null) нужно вызывать в обработчике AppState.changebackground, иначе пользователь будет висеть в списке онлайн-участников после сворачивания приложения.

Flutter: Y.js через JS runtime

Для Flutter нативного порта Y.js нет. Варианты:

  • flutter_js — запускает V8/QuickJS, весит ~5 МБ. Y.js работает, но производительность на больших документах оставляет желать.
  • Нативный Dart CRDT: crdt пакет от Cachapa — реализует LWW-CRDT, не совместим с Y.js по протоколу.
  • Rust FFI через yrs (Rust-реализация Y.js) + flutter_rust_bridge — наиболее производительный путь, но 4–6 недель только на биндинги.

Оценка

React Native + Y.js + кастомный SQLite-провайдер + Hocuspocus бэкенд: 6–10 недель. Flutter через yrs FFI: 10–16 недель. Включает: персистентность, awareness, reconnect-логику с exponential backoff, тесты на конфликты при одновременном редактировании. Стоимость рассчитывается индивидуально после анализа требований.