[WIP] Add in-app migration to the new Home Assistant app - #5511
Conversation
|
Found 1 unused localization strings in the codebase. Click to see detailsTo remove them, run the |
There was a problem hiding this comment.
Pull request overview
Adds an in-app migration flow to move servers (including tokens) and app configuration from the old Home Assistant iOS app (old Apple Developer account / bundle ID) to the new app, using a URL-fragment handoff with optional chunking and a full-screen guided UI on both the source and destination apps.
Changes:
- Introduces a complete App Migration feature set (payload encoding/decoding + chunking, URL formats, persisted chunk store, status/retirement).
- Adds SwiftUI flows and presenters for exporting/importing, plus Settings entry points and lifecycle URL handling.
- Adds localization and snapshot/unit tests covering the migration UI and core encoding/link/chunk behavior.
Reviewed changes
Copilot reviewed 74 out of 110 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| Tests/App/Container/AppMigration/AppMigrationAwaitingConfirmationView.test.swift | Snapshot coverage for the “awaiting confirmation” screen. |
| Tests/App/Container/AppMigration/AppMigrationChunk.test.swift | Unit tests for chunk wire format parsing/encoding. |
| Tests/App/Container/AppMigration/AppMigrationChunkStore.test.swift | Unit tests for persisted chunk assembly and session isolation. |
| Tests/App/Container/AppMigration/AppMigrationCompletedView.test.swift | Snapshot coverage for the source-app completion screen. |
| Tests/App/Container/AppMigration/AppMigrationFailureView.test.swift | Snapshot coverage for the source-app failure screen. |
| Tests/App/Container/AppMigration/AppMigrationImportCompletedView.test.swift | Snapshot coverage for the destination-app completion screen. |
| Tests/App/Container/AppMigration/AppMigrationImportFailureView.test.swift | Snapshot coverage for the destination-app failure screen. |
| Tests/App/Container/AppMigration/AppMigrationIntroView.test.swift | Snapshot coverage for the intro screen (server-count pluralization). |
| Tests/App/Container/AppMigration/AppMigrationLink.test.swift | Unit tests for handoff/continuation/completion URL round-trips and safety properties. |
| Tests/App/Container/AppMigration/AppMigrationNeedsInstallView.test.swift | Snapshot coverage for “needs install” screen. |
| Tests/App/Container/AppMigration/AppMigrationPayloadCoder.test.swift | Unit tests for chunking thresholds and payload decoding errors. |
| Tests/App/Container/AppMigration/AppMigrationProgressView.test.swift | Snapshot coverage for export/import progress step lists and transfer bar. |
| Tests/App/Container/AppMigration/AppMigrationStepRow.test.swift | Snapshot coverage for step-row visual states. |
| Sources/App/Container/AppMigration/AppMigrationAwaitingConfirmationView.swift | Source-app “waiting for destination app to finish” UI. |
| Sources/App/Container/AppMigration/AppMigrationBottomActions.swift | Shared bottom action buttons for list-based migration screens. |
| Sources/App/Container/AppMigration/AppMigrationCompletedView.swift | Source-app completion UI and messaging. |
| Sources/App/Container/AppMigration/AppMigrationCompletionKey.swift | Notification key + notification name for completion acknowledgements. |
| Sources/App/Container/AppMigration/AppMigrationContinuationKey.swift | Notification keys + notification names for chunk continuation flow. |
| Sources/App/Container/AppMigration/AppMigrationCoverModifier.swift | Window-root full-screen cover routing for export/import flows. |
| Sources/App/Container/AppMigration/AppMigrationDisclosureRow.swift | Row component for intro “included/excluded” lists. |
| Sources/App/Container/AppMigration/AppMigrationExportStep.swift | Export-side step definitions for the progress list. |
| Sources/App/Container/AppMigration/AppMigrationExporter.swift | Source-app packaging (servers + configuration + metadata) into migration payload. |
| Sources/App/Container/AppMigration/AppMigrationFailureView.swift | Source-app failure UI with retry/later actions and Settings shortcut. |
| Sources/App/Container/AppMigration/AppMigrationFlowView.swift | Source-app full migration flow coordinator view. |
| Sources/App/Container/AppMigration/AppMigrationHaptics.swift | Shared haptics for step progress, chunk transfer, success/failure. |
| Sources/App/Container/AppMigration/AppMigrationHeaderRow.swift | Common header (illustration + title + descriptions) for migration screens. |
| Sources/App/Container/AppMigration/AppMigrationIllustration.swift | Shared symbol illustration component for the flow. |
| Sources/App/Container/AppMigration/AppMigrationImportCompletedView.swift | Destination-app completion UI. |
| Sources/App/Container/AppMigration/AppMigrationImportFailureView.swift | Destination-app failure UI. |
| Sources/App/Container/AppMigration/AppMigrationImportFlowView.swift | Destination-app full-screen import flow wrapper. |
| Sources/App/Container/AppMigration/AppMigrationImporter.swift | Destination-side application of servers/config and callback acknowledgement. |
| Sources/App/Container/AppMigration/AppMigrationImportPause.swift | Control-flow error used to pause import while awaiting more chunks. |
| Sources/App/Container/AppMigration/AppMigrationImportPhase.swift | Destination-side phase model. |
| Sources/App/Container/AppMigration/AppMigrationImportStep.swift | Destination-side step definitions for the progress list. |
| Sources/App/Container/AppMigration/AppMigrationImportSummary.swift | Summary of what was imported for destination completion UI. |
| Sources/App/Container/AppMigration/AppMigrationImportViewModel.swift | Destination-side import state machine, chunk ingestion, and continuation requests. |
| Sources/App/Container/AppMigration/AppMigrationIntroView.swift | Source-side intro/explainer UI before export begins. |
| Sources/App/Container/AppMigration/AppMigrationNeedsInstallView.swift | Source-side UI when destination app isn’t installed yet. |
| Sources/App/Container/AppMigration/AppMigrationPhase.swift | Source-side phase model. |
| Sources/App/Container/AppMigration/AppMigrationPresenter.swift | Singleton presenter routing URLs and deciding which migration flow to show. |
| Sources/App/Container/AppMigration/AppMigrationPresentation.swift | Identifiable presentation state for full-screen covers. |
| Sources/App/Container/AppMigration/AppMigrationProgressView.swift | Shared progress-list UI for export/import steps + optional transfer bar. |
| Sources/App/Container/AppMigration/AppMigrationRetirement.swift | Source-app retirement behavior after destination confirms import. |
| Sources/App/Container/AppMigration/AppMigrationRowIcon.swift | Shared leading icon layout for migration rows. |
| Sources/App/Container/AppMigration/AppMigrationScreenLock.swift | Idle-timer disabling counter for long-running migrations. |
| Sources/App/Container/AppMigration/AppMigrationScreenLockModifier.swift | View modifier hooking screen-lock acquire/release to view lifetime. |
| Sources/App/Container/AppMigration/AppMigrationSettingsSection.swift | Settings entry point for migration (only on source + when configured). |
| Sources/App/Container/AppMigration/AppMigrationSettingsShortcutButton.swift | Settings shortcut shown on failure screen. |
| Sources/App/Container/AppMigration/AppMigrationStepDescribing.swift | Shared protocol for step models shown in progress lists. |
| Sources/App/Container/AppMigration/AppMigrationStepRow.swift | Step-row UI with animated indicator and accessibility status. |
| Sources/App/Container/AppMigration/AppMigrationStepState.swift | Step state enum for progress UI. |
| Sources/App/Container/AppMigration/AppMigrationTransferProgress.swift | Transfer progress model (completed/total + fraction). |
| Sources/App/Container/AppMigration/AppMigrationTransferProgressView.swift | Transfer bar shown when chunking is in play. |
| Sources/App/Container/AppMigration/AppMigrationViewModel.swift | Source-side export state machine (packaging + handoff + confirmation). |
| Sources/App/Container/AppMigration/View+AppMigrationCover.swift | Convenience modifier to attach migration cover at window root. |
| Sources/App/Container/AppMigration/View+AppMigrationHeaderRowStyle.swift | List-row styling helper for migration header row. |
| Sources/App/Container/AppMigration/View+AppMigrationKeepsScreenAwake.swift | Convenience modifier to keep screen awake during migration views. |
| Sources/App/HAApp.swift | Hooks migration routing into onOpenURL / universal link handling and applies window-root cover. |
| Sources/App/Resources/en.lproj/Localizable.strings | Adds all English strings for the migration UI and errors. |
| Sources/App/Resources/Info.plist | Adds destination URL scheme to LSApplicationQueriesSchemes for install-detection. |
| Sources/App/Settings/ConfigurationTransfer/AppConfigurationTransfer.swift | Adds import/export helpers for in-memory (Data) configuration transfer for migration. |
| Sources/App/Settings/Settings/SettingsView.swift | Adds migration section entry point into Settings (source-only, when configured). |
| Sources/Shared/Common/AppMigration/AppMigrationChunk.swift | Defines chunk wire format for sliced payload handoff. |
| Sources/Shared/Common/AppMigration/AppMigrationChunkStore.swift | Persists and assembles received chunks across app switches. |
| Sources/Shared/Common/AppMigration/AppMigrationCodingError.swift | Localized error types for payload encoding/decoding and size limits. |
| Sources/Shared/Common/AppMigration/AppMigrationConstants.swift | Central identifiers + safety limits + “configured” gate. |
| Sources/Shared/Common/AppMigration/AppMigrationLink.swift | Constructs/parses handoff, continuation, and completion URLs. |
| Sources/Shared/Common/AppMigration/AppMigrationPayload.swift | Defines the migration payload envelope. |
| Sources/Shared/Common/AppMigration/AppMigrationPayloadCoder.swift | Compresses/encodes payload to base64url, slices into chunks, and decodes обратно. |
| Sources/Shared/Common/AppMigration/AppMigrationRole.swift | Determines whether the running build is source vs destination. |
| Sources/Shared/Common/AppMigration/AppMigrationStatus.swift | Persists migration state (prompt snooze, handed-off, imported) in shared prefs. |
| Sources/Shared/Common/Extensions/Data+Base64URL.swift | Adds base64url encode/decode helpers used by migration payload transport. |
| Sources/Shared/Resources/Swiftgen/Strings.swift | SwiftGen output updated with L10n.AppMigration.* accessors. |
| APP_MIGRATION.md | End-to-end operational checklist and rationale for moving to a new Apple Developer account. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| public static var current: AppMigrationRole { | ||
| AppConstants.BundleID.hasPrefix(AppMigrationConstants.sourceBundleID) ? .source : .destination | ||
| } |
| let value = components.queryItems? | ||
| .first { $0.name == AppMigrationConstants.importedServerCountQueryItem }? | ||
| .value | ||
| return value.flatMap(Int.init) ?? 0 | ||
| } |
| func run() async { | ||
| stepStates = [:] | ||
| phase = .running | ||
|
|
||
| transfer = chunk.total > 1 ? .init(completed: chunk.index, total: chunk.total) : nil |
Apple's App Transfer is not available for this app, so the replacement ships under a new developer account with a new bundle ID. This adds the in-app flow that moves a user's servers and configuration across. The flow is hidden until the new app's identifiers are filled in, so it has no effect on the current app.
AI Policy
Select exactly one option that describes AI usage in this contribution:
Summary
Apple's App Transfer is not available for this app, so the replacement ships under a new developer account with a new bundle ID. This adds the in-app flow that moves a user's servers and configuration from the old app to the new one.
The old app packages its servers (connection details and tokens) and its configuration (widgets, watch, CarPlay, quick actions, kiosk, notification categories, NFC tags, reminders sync, app settings), compresses it, and hands it to the new app in a URL fragment. The new app restores the servers first, then the configuration, then tells the old app it can stand down. Payloads too large for a single link are handed over a slice at a time.
Every identifier for the new app is a placeholder in
AppMigrationConstants, and the whole flow stays hidden until they are filled in, so this has no effect on the current app.Screenshots
Recorded as snapshot tests in light and dark mode:

Tests/App/Container/AppMigration/__Snapshots__/.Link to pull request in Documentation repository
Documentation: home-assistant/companion.home-assistant#
Any other notes