Інтеграція 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
    792
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    495

Інтеграція 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 містить внутрішній steyт-vector — Map<clientId, maxClock>. При підключенні двох клієнтів вони обмінюються своїми стейт-векторами й запрашують тільки дельту: Y.encodeStateAsUpdateV2(doc, remoteStateVector). Це дифференційний протокол — при реконнекті не потрібно передавати весь документ.

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

Провайдер Транспорт Особливості
y-websocket WebSocket Офіційний, є серверна частина
y-webrtc WebRTC DataChannel P2P, немає у RN без polyfill
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. Без цього у prodacшені у вас швидко закінчиться місце на пристрої, а транзакції у SQLite почнуть блокувати JS-тред.

Серверна частина: y-websocket vs власний сервер

Офіційний y-websocket сервер мінімалістичен — зберігає документи у пам'яті. Для prodacшену потрібно:

  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 після кожного розмонтування, і стейт-vector сервера накопичуватиме мертві записи. Фіксу: зберігати ydoc у ref або глобальному steyте, не пересоздавати.

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

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, тести на конфлікти при одночасному редагуванні. Вартість розраховується індивідуально після аналізу вимог.