Реализация переноса данных при смене устройства (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 недели. Стоимость рассчитывается индивидуально.







