Реалізація передачі завдання між пристроями у мобільних застосунках
Користувач почав заповнювати форму на телефоні, відкрив ноутбук—форма з'являється з тими ж даними на тому ж кроці. Це Handoff, і Apple має його з коробки через NSUserActivity. Для крос-платформних та крос-екосистемних сценаріїв (iOS → Android, мобільний → десктоп) побудуйте свій.
Apple Handoff: коли реалізація не потрібна
NSUserActivity—механізм передачі контексту завдання між пристроями Apple через iCloud. Підтримує iOS → macOS, macOS → iOS, iOS → iPadOS.
У React Native через нативний модуль:
// iOS Native Module
let activity = NSUserActivity(activityType: "com.yourapp.editDocument")
activity.title = "Редагування документа"
activity.userInfo = ["documentId": documentId, "scrollPosition": scrollY, "formData": formData]
activity.isEligibleForHandoff = true
activity.becomeCurrent()
На приймаючій стороні macOS/iOS AppDelegate отримує application(_:continue:restorationHandler:) з цією NSUserActivity. Дані у userInfo—словник, обмежено ~256 КБ.
Обмеження Handoff: тільки Apple, один аутентифікований акаунт, Bluetooth + Wi-Fi обов'язкові. Для користувачів з Android-телефоном та Windows-ноутбуком—не працює.
Крос-платформенний Handoff: серверна сесія
Універсальний підхід: сесія завдання зберігається на сервері, прив'язана до акаунту, а не пристрою.
type TaskSession = {
sessionId: string;
userId: string;
taskType: 'checkout' | 'editDocument' | 'formFill' | 'mediaUpload';
state: Record<string, unknown>; // серіалізований стан завдання
activeDeviceId: string;
updatedAt: number;
expiresAt: number;
};
Ключові моменти:
Гранулярний стан: не зберігайте весь Redux store. Зберігайте тільки те, що потрібно для відновлення завдання. Для форми: { step: 2, fields: { name: 'Іван', email: 'ivan@...' }, validationState: {...} }. Ніколи не зберігайте конфіденційні дані (CVV, PIN).
Розв'язання конфліктів: користувач працює на двох пристроях одночасно. Остання запис виконує (LWW)—достатньо для більшості випадків. Для складних завдань—використовуйте vector clocks.
Виявлення активних сесій: при відкриванні застосунку перевірте неповні сесії та запропонуйте продовжити.
Автоматичне збереження стану завдання
const useTaskHandoff = (taskType: string) => {
const [sessionId] = useState(() => generateSessionId());
const debouncedSave = useMemo(
() => debounce(async (state: Record<string, unknown>) => {
await api.handoff.saveSession({
sessionId,
taskType,
state,
deviceId: getDeviceId(),
});
}, 1000),
[sessionId, taskType]
);
// Викличте при кожній зміні стану
useEffect(() => {
debouncedSave(currentTaskState);
return () => debouncedSave.cancel();
}, [currentTaskState]);
// Відновлення при запуску
const restoreSession = useCallback(async (incomingSessionId: string) => {
const session = await api.handoff.getSession(incomingSessionId);
if (session) restoreTaskState(session.state);
}, []);
return { restoreSession };
};
Debounce на 1 секунду—не зберігайте при кожному натиску клавіші. Активний ввід = 60+ запитів за хвилину, непотрібно для сервера та батареї.
Deep Link для відкриття завдання на іншому пристрої
Як другий пристрій дізнається про сесію? Варіанти:
-
Push-сповіщення: коли пристрій переходить у неактивний режим, отправте silent push з
sessionId. Другий пристрій показує банер «Продовжити на цьому пристрої?». -
Deep link: користувач явно копіює посилання
yourapp://handoff/session/abc123та відкриває на іншому пристрої. -
QR-код: показуйте QR з
sessionId, скануйте другим пристроєм. - Автоматично: при відкриванні застосунку на другому пристрої—запитайте активні сесії та запропонуйте список неповних завдань.
Варіант 4—найкращий UX, але вимагає чіткої політики приватності: користувач повинен бачити, що його неповні завдання зберігаються на сервері, і мати можливість їх видалити.
Типові завдання для Handoff
- Багатосторінкові форми (оформлення покупки, анкети, заявки)
- Редагування документів/постів з проміжним станом
- Завантаження медіафайлів (почали на телефоні, продовжуємо на планшеті з більшим екраном)
- Ігрові сесії з збереженням прогресу між пристроями
Не реалізовуйте Handoff для: одноекранних завдань, завдань без чіткого «кроку завершення», завдань з конфіденційними даними без E2E-шифрування.
Оцінка часу
Серверна сесія завдань з deep link, автозбереженням та UI для відновлення: 3–5 тижнів. З Apple NSUserActivity + крос-платформним fallback: 4–6 тижнів.







