Реалізація cross-device copy-paste у мобільних застосунках
Скопіювати текст на телефоні — вставити на ноутбуці. Простою в теорії, але реалізація натрапляє на обмеження буфера обміну на обох платформах і вимагає серверного компонента. Нативний універсальний буфер обміну (Apple Universal Clipboard) працює тільки в екосистемі Apple з увімкненим Handoff. Для крос-платформних сценаріїв (iOS → Android, мобільний → десктоп) потрібен інший підхід.
Apple Universal Clipboard: коли він працює автоматично
Якщо обидва пристрої — Apple, використовують той самий Apple ID і Handoff увімкнений, UIPasteboard.general на iOS автоматично синхронізується через iCloud. Реалізація не потрібна. Але це працює тільки для тексту та зображень з затримкою 30–60 секунд і тільки в екосистемі Apple.
Для застосунків, які повинні працювати незалежно від платформи, побудуйте власний механізм.
Архітектура крос-пристрійного буфера обміну
Мінімальна схема: сервер зберігає буфер користувача, клієнти синхронізуються через WebSocket або polling.
// Серверна частина: просте сховище з TTL
type ClipboardEntry = {
userId: string;
content: string;
contentType: 'text' | 'image' | 'file';
mimeType?: string;
expiresAt: number; // unix timestamp
deviceId: string; // вихідний пристрій
};
TTL обов'язковий. Буфер обміну не повинен зберігати дані вічно: конфіденційна інформація (паролі, токени, дані карти), яку користувач копіює, повинна видалятися. 30–120 хвилин — розумний TTL для більшості сценаріїв.
Нативний буфер обміну в React Native
import Clipboard from '@react-native-clipboard/clipboard';
// Копіювання з синхронізацією на сервер
const copyToCloudClipboard = async (text: string) => {
// Спочатку в локальний буфер — миттєво
await Clipboard.setString(text);
// Паралельна синхронізація сервера
await api.clipboard.push({
content: text,
contentType: 'text',
deviceId: getDeviceId(),
});
};
// Вставка: спочатку перевіримо хмарний буфер
const pasteFromCloudClipboard = async (): Promise<string> => {
const [localContent, cloudEntry] = await Promise.all([
Clipboard.getString(),
api.clipboard.getLatest(),
]);
// Виберемо свіжіший
if (cloudEntry && cloudEntry.updatedAt > localTimestamp) {
return cloudEntry.content;
}
return localContent;
};
Контент, крім тексту
Зображення: завантажте в S3/CDN, зберігайте тільки URL + метадані у буфері. Розмір зображення у буфері — до 10 МБ, застосуйте обмеження на клієнті. Файли: аналогічно, URL для завантаження.
На iOS UIPasteboard підтримує типи через UTType. При копіюванні зображень у нативні застосунки, використовуйте нативний модуль, який читає UIPasteboard.general.image та завантажує у хмарний буфер. @react-native-clipboard/clipboard у базовій комплектації не підтримує зображення—патчить або напишіть нативний модуль.
Сповіщення про новий контент у буфері
Push-сповіщення при появі нового контенту у буфері—погана UX: користувач щойно його скопіював, чому сповіщення? Краще: індикатор у інтерфейсі при відкриванні застосунку або безшумна синхронізація через background fetch.
WebSocket підхід: при відкриванні застосунку підпишіться на канал користувача. Коли новий контент буфера обміну прибуває з іншого пристрою, показуйте non-blocking банер «Скопійовано з MacBook: "текст..."».
Безпека: що не повинно потрапляти у хмарний буфер
Виявіть конфіденційний контент перед відправкою на сервер:
- Regex для номерів карток (Luhn валідація): не відправляйте, очистіть через 30 сек.
- Regex для паролів у форматі
password: xyz—не відправляйте. - Дуже довгі рядки (>100 КБ)—вірогідно не призначені для буфера.
Шифрування: зашифруйте контент буфера обміну ключем, похідним від пароля користувача або пристрійного ключа. Сервер зберігає зашифрований blob—не може читати вміст.
Оцінка часу
Крос-пристрійний буфер обміну (текст + зображення) з E2E-шифруванням, WebSocket-сповіщеннями та TTL: 3–5 тижнів.







