Реалізація переноса даних при смене пристрою (Device Migration)
Користувач купив новий телефон. Якщо перенос даних з додатку не працює — він починає заново: налаштовує профіль, теряет історію, переустановлює покупки. Рейтинг додатку в App Store це не піднімає. Device migration — це не один механізм, а набір інструментів з різними компромісами.
Платформені інструменти
iOS: QuickStart (iPhone-to-iPhone прямій перенос через Bluetooth/WiFi) та iCloud Backup переносять дані додатку автоматично, якщо додаток їх зберігає коректно. Дані у Documents та Application Support включаються у резервну копію за умовчанням. Keychain з kSecAttrAccessible = kSecAttrAccessibleAfterFirstUnlock — переносяться при iCloud Backup при умові kSecAttrSynchronizable = true.
Android: Google One Backup + Auto Backup переносять SharedPreferences, Room БД, файли з getDataDir(). Налаштування у AndroidManifest.xml:
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules">
<!-- res/xml/data_extraction_rules.xml (Android 12+) -->
<data-extraction-rules>
<cloud-backup>
<include domain="database" path="app.db"/>
<include domain="sharedpref" path="user_prefs.xml"/>
<exclude domain="database" path="http_cache.db"/>
<exclude domain="file" path="temp/"/>
</cloud-backup>
</data-extraction-rules>
Серверний перенос через аккаунт
Найнадійніший підхід — всё важне зберігається на сервері, прив'язане до аккаунту. Користувач логінится на новому пристрої — отримує всі дані. Для додатків з авторизацією це стандарт.
Що повинно бути на сервері:
- Профіль користувача
- Історія дій
- Покупки (обов'язково — для відновлення)
- Користувальницький контент
- Налаштування, якщо вони впливають на бекенд-логіку
Що можна не синхронізувати:
- Локальні налаштування UI (тема, розмір шрифту) — дешевше дати вибрати заново
- Кеш (буде відновлений автоматично)
- Тимчасові файли
QR-код або код міграції
Для додатків без учітних записів — прямій перенос через QR або числовий код. Принцип: старе пристрій генерує тимчасовий токен або зашифрований payload, нов пристрій його сканує.
// Генерація кода міграції
class MigrationCodeGenerator(private val exportManager: DataExportManager) {
suspend fun generateMigrationCode(): MigrationCode {
val exportedData = exportManager.exportUserData()
val encryptedPayload = encryptWithTemporaryKey(exportedData)
// Або відправляємо на сервер та отримуємо короткий код
val code = api.createMigrationSession(
payload = encryptedPayload,
expiresIn = 10 * 60 // 10 хвилин
)
return MigrationCode(
code = code.shortCode, // "ABCD-1234"
qrData = code.qrPayload, // для QR-кода
expiresAt = code.expiresAt
)
}
}
// Імпорт на новому пристрої
suspend fun importFromCode(code: String): ImportResult {
return try {
val session = api.getMigrationSession(code)
if (session.isExpired) return ImportResult.Expired
val data = decryptPayload(session.encryptedPayload, session.tempKey)
importManager.applyUserData(data)
api.invalidateMigrationSession(code) // одноразовий — сразу інвалідуємо
ImportResult.Success
} catch (e: Exception) {
ImportResult.Error(e.message)
}
}
Прямій peer-to-peer перенос
Для конфіденційних даних, які не повинні проходити через сервер — прямій канал між пристроями:
iOS: MultipeerConnectivity framework — WiFi Direct або Bluetooth, без інтернету. Android: Nearby Connections API (Google Play Services) — WiFi, Bluetooth, NFC.
// iOS: інніціація сесії MultipeerConnectivity
let peerID = MCPeerID(displayName: UIDevice.current.name)
let session = MCSession(peer: peerID, securityIdentity: nil, encryptionPreference: .required)
let advertiser = MCNearbyServiceAdvertiser(peer: peerID,
discoveryInfo: ["appVersion": Bundle.main.appVersionString],
serviceType: "myapp-migrate")
Для великих обсягів даних (фото, файли) — тільки прямій канал, не через сервер. Швидкість передачі по WiFi Direct — 20-50 МБ/с проти 1-5 МБ/с через інтернет.
Відновлення покупок
In-app purchases — окрема історія. Apple та Google зберігають історію покупок на своїй стороні.
// iOS: відновлення покупок StoreKit 2
for await result in Transaction.currentEntitlements {
switch result {
case .verified(let transaction):
await updatePurchasedProducts(transaction.productID)
case .unverified:
break // підозрівна транзакція
}
}
// Android: BillingClient
billingClient.queryPurchasesAsync(
QueryPurchasesParams.newBuilder()
.setProductType(BillingClient.ProductType.SUBS)
.build()
) { billingResult, purchases ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
purchases.forEach { purchase ->
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
grantEntitlement(purchase.products)
}
}
}
}
Кнопка «Відновити покупки» — обов'язкова за гайдлайнами App Store.
Типічні помилки
Перенос без валідації версії. Дані з додатку v1.0 імпортуються у v3.5 без міграції схеми — крах або некоректне стан.
Незашифрований QR. QR-код містить plaintext дані користувача — хто-то сфотографував чужий екран.
Не інвалідується токен міграції. Код можна використовувати повторно — дані утекли на третій пристрій.
Реалізація переноса даних з QR-кодом, серверною синхронізацією та відновленням покупок: 2–3 тижні. Вартість рассчитывается індивідуально.







