appmigrationkit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAppMigrationKit
AppMigrationKit
One-time cross-platform data transfer for app resources. Enables apps to
export data from one platform (e.g., Android) and import it on iOS during
device setup or onboarding. iOS 26+ / iPadOS 26+ / Swift 6.3.
Beta-sensitive. AppMigrationKit is new in iOS 26 and may change before GM. Re-check current Apple documentation before relying on specific API details.
AppMigrationKit uses an app extension model. The system orchestrates the
transfer between devices. The app provides an extension conforming to export
and import protocols, and the system calls that extension at the appropriate
time. The app itself never manages the network connection between devices.
实现应用资源的一次性跨平台数据传输。支持应用在设备设置或新手引导阶段,从其他平台(例如Android)导出数据并导入到iOS系统。适配iOS 26+ / iPadOS 26+ / Swift 6.3。
测试版注意事项: AppMigrationKit是iOS 26新增的框架,在正式版(GM)发布前可能会发生变更。在依赖具体API细节前,请重新查阅最新的Apple官方文档。
AppMigrationKit采用应用扩展模型,由系统编排不同设备之间的传输流程。应用需要提供符合导出和导入协议的扩展,系统会在合适的时机调用该扩展,应用本身无需管理设备之间的网络连接。
Contents
目录
Architecture Overview
架构概述
AppMigrationKit operates through three layers:
- App extension -- An conforming type that the system invokes during migration. It handles data export and import.
AppMigrationExtension - System orchestration -- The OS manages the device-to-device session, transport, and scheduling. The extension does not control when it runs.
- Containing app -- After migration completes, the app checks
on first launch to determine whether migration occurred and whether it succeeded.
MigrationStatus.importStatus
Key types:
| Type | Role |
|---|---|
| Protocol for the app extension entry point |
| Protocol for exporting files via archiver |
| Simplified export protocol (no custom options) |
| Protocol for importing files on the destination |
| Streams files into the export archive |
| Access to the containing app's data directories |
| Check import result from the containing app |
| Identifies the other device's platform (e.g., |
| Identifies the source app by store and bundle ID |
| Test-only actor for validating export/import logic |
AppMigrationKit通过三层结构运行:
- 应用扩展 -- 遵循协议的类型,系统会在迁移过程中调用该扩展处理数据的导出和导入。
AppMigrationExtension - 系统编排层 -- 操作系统管理设备间会话、传输和调度,扩展无法控制自身的运行时机。
- 宿主应用 -- 迁移完成后,应用在首次启动时检查来判断是否发生过迁移以及迁移是否成功。
MigrationStatus.importStatus
核心类型:
| 类型 | 作用 |
|---|---|
| 应用扩展入口点的协议 |
| 通过归档器导出文件的协议 |
| 简化版导出协议(无自定义选项) |
| 在目标设备上导入文件的协议 |
| 将文件流式写入导出归档包 |
| 访问宿主应用的数据目录 |
| 从宿主应用中查询导入结果 |
| 标识另一台设备的平台(例如 |
| 通过应用商店和包ID标识源应用 |
| 仅用于测试的Actor,用于验证导出/导入逻辑 |
Setup and Entitlements
设置与权限
Entitlement
权限声明
The app extension requires the
entitlement. Its value is a single-element string array containing the bundle
identifier of the containing app:
com.apple.developer.app-migration.data-container-accessxml
<key>com.apple.developer.app-migration.data-container-access</key>
<array>
<string>com.example.myapp</string>
</array>No other values are valid. This entitlement grants the extension read access
to the containing app's data container during export and write access during
import.
应用扩展需要权限,其值为仅包含一个字符串元素的数组,内容为宿主应用的包ID:
com.apple.developer.app-migration.data-container-accessxml
<key>com.apple.developer.app-migration.data-container-access</key>
<array>
<string>com.example.myapp</string>
</array>不支持其他值。该权限授予扩展在导出阶段读取宿主应用数据容器、在导入阶段写入数据容器的权限。
Extension Target
扩展Target
Add a new App Extension target to the Xcode project. The extension conforms
to one or more of the migration protocols (,
, ).
ResourcesExportingWithOptionsResourcesExportingResourcesImporting在Xcode项目中添加一个新的App Extension target,扩展需要遵循一个或多个迁移协议(、、)。
ResourcesExportingWithOptionsResourcesExportingResourcesImportingApp Migration Extension
App迁移扩展
The extension entry point conforms to . During
migration, the system prevents launching the containing app and its other
extensions to ensure exclusive data access.
AppMigrationExtension扩展的入口点遵循协议。迁移过程中,系统会禁止启动宿主应用及其它扩展,以保证独占数据访问权限。
AppMigrationExtensionAccessing the Data Container
访问数据容器
The extension accesses the containing app's files through :
appContainerswift
import AppMigrationKit
struct MyMigrationExtension: ResourcesExportingWithOptions, ResourcesImporting {
// Access the containing app's directories
let container = appContainer
// container.bundleIdentifier -- app's bundle ID
// container.containerRootDirectory -- root of the app container
// container.documentsDirectory -- Documents/
// container.applicationSupportDirectory -- Application Support/
}MigrationDataContainercontainerRootDirectorydocumentsDirectoryapplicationSupportDirectoryURL扩展通过访问宿主应用的文件:
appContainerswift
import AppMigrationKit
struct MyMigrationExtension: ResourcesExportingWithOptions, ResourcesImporting {
// 访问宿主应用的目录
let container = appContainer
// container.bundleIdentifier -- 应用的包ID
// container.containerRootDirectory -- 应用容器的根目录
// container.documentsDirectory -- Documents/目录
// container.applicationSupportDirectory -- Application Support/目录
}MigrationDataContainercontainerRootDirectorydocumentsDirectoryapplicationSupportDirectoryURLExporting Resources
导出资源
Conform to (or for no
custom options) to package files for transfer. The system calls
with a and a
.
ResourcesExportingWithOptionsResourcesExportingexportResources(to:request:)ResourcesArchiverMigrationRequestWithOptions遵循协议(无需自定义选项时可遵循协议)来打包待传输的文件。系统会传入和参数,调用方法。
ResourcesExportingWithOptionsResourcesExportingResourcesArchiverMigrationRequestWithOptionsexportResources(to:request:)Declaring Export Properties
声明导出属性
swift
struct MyMigrationExtension: ResourcesExportingWithOptions {
typealias OptionsType = MigrationDefaultSupportedOptions
var resourcesSizeEstimate: Int {
// Return estimated total bytes of exported data
calculateExportSize()
}
var resourcesVersion: String {
"1.0"
}
var resourcesCompressible: Bool {
true // Let the system compress during transport
}
}- -- Estimated total bytes. The system uses this for progress UI and free-space checks.
resourcesSizeEstimate - -- Format version string. The import side receives this to handle versioned data formats.
resourcesVersion - -- When
resourcesCompressible, the archiver may compress files during transport.true
swift
struct MyMigrationExtension: ResourcesExportingWithOptions {
typealias OptionsType = MigrationDefaultSupportedOptions
var resourcesSizeEstimate: Int {
// 返回导出数据的预估总字节数
calculateExportSize()
}
var resourcesVersion: String {
"1.0"
}
var resourcesCompressible: Bool {
true // 允许系统在传输过程中压缩数据
}
}- -- 预估总字节数,系统会用该值计算进度UI和剩余存储空间检查。
resourcesSizeEstimate - -- 格式版本字符串,导入端会接收该值来处理不同版本的数据格式。
resourcesVersion - -- 设为
resourcesCompressible时,归档器可以在传输过程中压缩文件。true
Implementing Export
实现导出逻辑
swift
func exportResources(
to archiver: sending ResourcesArchiver,
request: MigrationRequestWithOptions<MigrationDefaultSupportedOptions>
) async throws {
let docsDir = appContainer.documentsDirectory
// Check destination platform if needed
if request.destinationPlatform == .android {
// Platform-specific export logic
}
// Append files one at a time -- make continuous progress
let userDataURL = docsDir.appending(path: "user_data.json")
try await archiver.appendItem(at: userDataURL)
// Append with a custom archive path
let settingsURL = docsDir.appending(path: "settings.plist")
try await archiver.appendItem(at: settingsURL, pathInArchive: "preferences/settings.plist")
// Append a directory
let photosDir = docsDir.appending(path: "photos")
try await archiver.appendItem(at: photosDir, pathInArchive: "media/photos")
}The archiver streams files incrementally. Call
repeatedly as each resource is ready. The system may terminate the extension
if it appears hung, so avoid long gaps between append calls.
appendItem(at:pathInArchive:)swift
func exportResources(
to archiver: sending ResourcesArchiver,
request: MigrationRequestWithOptions<MigrationDefaultSupportedOptions>
) async throws {
let docsDir = appContainer.documentsDirectory
// 按需检查目标平台
if request.destinationPlatform == .android {
// 平台特定的导出逻辑
}
// 逐个添加文件 -- 保持进度持续更新
let userDataURL = docsDir.appending(path: "user_data.json")
try await archiver.appendItem(at: userDataURL)
// 自定义归档路径添加文件
let settingsURL = docsDir.appending(path: "settings.plist")
try await archiver.appendItem(at: settingsURL, pathInArchive: "preferences/settings.plist")
// 添加目录
let photosDir = docsDir.appending(path: "photos")
try await archiver.appendItem(at: photosDir, pathInArchive: "media/photos")
}归档器会增量流式处理文件,每准备好一个资源就调用一次。如果扩展长时间无响应,系统可能会终止扩展,因此要避免两次append调用之间间隔过长。
appendItem(at:pathInArchive:)Cancellation
取消处理
ResourcesArchiverResourcesArchiverMigration Platform
迁移平台
MigrationRequestWithOptionsdestinationPlatformMigrationPlatformswift
if request.destinationPlatform == .android {
// Export in a format the Android app expects
}MigrationPlatform.androidMigrationPlatform("customPlatform")MigrationRequestWithOptionsdestinationPlatformMigrationPlatformswift
if request.destinationPlatform == .android {
// 导出为Android应用期望的格式
}MigrationPlatform.androidMigrationPlatform("customPlatform")Importing Resources
导入资源
Conform to to receive transferred files on the
destination device. The system calls after
app installation but before the app is launchable.
ResourcesImportingimportResources(at:request:)swift
struct MyMigrationExtension: ResourcesImporting {
func importResources(
at importedDataURL: URL,
request: ResourcesImportRequest
) async throws {
let sourceVersion = request.sourceVersion
let sourceApp = request.sourceAppIdentifier
// sourceApp.platform -- e.g., .android
// sourceApp.bundleIdentifier -- source app's bundle ID
// sourceApp.storeIdentifier -- e.g., .googlePlay
// Copy imported files into the app container
let docsDir = appContainer.documentsDirectory
let userData = importedDataURL.appending(path: "user_data.json")
if FileManager.default.fileExists(atPath: userData.path()) {
try FileManager.default.copyItem(
at: userData,
to: docsDir.appending(path: "user_data.json")
)
}
}
}遵循协议在目标设备上接收传输的文件。系统会在应用安装完成后、应用可启动前调用方法。
ResourcesImportingimportResources(at:request:)swift
struct MyMigrationExtension: ResourcesImporting {
func importResources(
at importedDataURL: URL,
request: ResourcesImportRequest
) async throws {
let sourceVersion = request.sourceVersion
let sourceApp = request.sourceAppIdentifier
// sourceApp.platform -- 例如 .android
// sourceApp.bundleIdentifier -- 源应用的包ID
// sourceApp.storeIdentifier -- 例如 .googlePlay
// 将导入的文件复制到应用容器
let docsDir = appContainer.documentsDirectory
let userData = importedDataURL.appending(path: "user_data.json")
if FileManager.default.fileExists(atPath: userData.path()) {
try FileManager.default.copyItem(
at: userData,
to: docsDir.appending(path: "user_data.json")
)
}
}
}Error Handling During Import
导入过程中的错误处理
On import error, the system clears the containing app's data container to
prevent partial state. However, app group containers are not cleared. The
import implementation should clear any app group containers before writing
imported content:
swift
func importResources(
at importedDataURL: URL,
request: ResourcesImportRequest
) async throws {
// Clear shared app group data first
let groupURL = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: "group.com.example.myapp"
)
if let groupURL {
try? FileManager.default.removeItem(at: groupURL.appending(path: "shared_data"))
}
// Then import
try await performImport(from: importedDataURL)
}如果导入出错,系统会清空宿主应用的数据容器以避免部分状态残留,但应用组容器不会被清空。因此导入实现应该在写入导入内容前先清空所有应用组容器:
swift
func importResources(
at importedDataURL: URL,
request: ResourcesImportRequest
) async throws {
// 首先清空共享应用组数据
let groupURL = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: "group.com.example.myapp"
)
if let groupURL {
try? FileManager.default.removeItem(at: groupURL.appending(path: "shared_data"))
}
// 然后执行导入
try await performImport(from: importedDataURL)
}Source App Identifier
源应用标识
ResourcesImportRequestsourceAppIdentifierMigrationAppIdentifier- -- The source device's platform (e.g.,
platform).android - -- The source app's bundle identifier
bundleIdentifier - -- The app store (e.g.,
storeIdentifier).googlePlay
ResourcesImportRequestsourceAppIdentifierMigrationAppIdentifier- -- 源设备的平台(例如
platform).android - -- 源应用的包ID
bundleIdentifier - -- 应用商店标识(例如
storeIdentifier).googlePlay
Migration Status
迁移状态
After migration completes, the containing app checks the result on first
launch:
swift
import AppMigrationKit
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
if let status = MigrationStatus.importStatus {
switch status {
case .success:
showMigrationSuccessUI()
MigrationStatus.clearImportStatus()
case .failure(let error):
showMigrationFailureUI(error: error)
MigrationStatus.clearImportStatus()
}
}
return true
}- is
MigrationStatus.importStatusif no migration occurred.nil - Call after handling the result to prevent showing the notification on subsequent launches.
clearImportStatus() - The enum has two cases: and
.success..failure(any Error)
迁移完成后,宿主应用在首次启动时检查迁移结果:
swift
import AppMigrationKit
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
if let status = MigrationStatus.importStatus {
switch status {
case .success:
showMigrationSuccessUI()
MigrationStatus.clearImportStatus()
case .failure(let error):
showMigrationFailureUI(error: error)
MigrationStatus.clearImportStatus()
}
}
return true
}- 如果没有发生过迁移,为
MigrationStatus.importStatus。nil - 处理完结果后调用,避免后续启动时再次展示迁移通知。
clearImportStatus() - 枚举包含两个case:和
.success。.failure(any Error)
Progress Tracking
进度跟踪
The import side exposes a object via .
The system uses this to display transfer progress to the user. Update
incrementally during import:
ProgressresourcesImportProgresscompletedUnitCountswift
var resourcesImportProgress: Progress {
Progress(totalUnitCount: 100)
}
func importResources(
at importedDataURL: URL,
request: ResourcesImportRequest
) async throws {
let progress = resourcesImportProgress
let files = try FileManager.default.contentsOfDirectory(
at: importedDataURL, includingPropertiesForKeys: nil
)
let increment = Int64(100 / max(files.count, 1))
for file in files {
try processFile(file)
progress.completedUnitCount += increment
}
progress.completedUnitCount = 100
}导入端通过暴露对象,系统会用该对象向用户展示传输进度。导入过程中增量更新:
resourcesImportProgressProgresscompletedUnitCountswift
var resourcesImportProgress: Progress {
Progress(totalUnitCount: 100)
}
func importResources(
at importedDataURL: URL,
request: ResourcesImportRequest
) async throws {
let progress = resourcesImportProgress
let files = try FileManager.default.contentsOfDirectory(
at: importedDataURL, includingPropertiesForKeys: nil
)
let increment = Int64(100 / max(files.count, 1))
for file in files {
try processFile(file)
progress.completedUnitCount += increment
}
progress.completedUnitCount = 100
}Testing
测试
AppMigrationTesterswift
import Testing
import AppMigrationKit
@Test func testExportImportRoundTrip() async throws {
let tester = try await AppMigrationTester(platform: .android)
// Export
let result = try await tester.exportController.exportResources(
request: nil, progress: nil
)
#expect(result.exportProperties.uncompressedBytes > 0)
// Import the exported data
try await tester.importController.importResources(
from: result.extractedResourcesURL,
importRequest: nil, progress: nil
)
try await tester.importController.registerImportCompletion(with: .success)
}DeviceToDeviceExportPropertiesuncompressedBytescompressedBytessizeEstimateversionSee references/appmigrationkit-patterns.md for additional test patterns.
AppMigrationTesterswift
import Testing
import AppMigrationKit
@Test func testExportImportRoundTrip() async throws {
let tester = try await AppMigrationTester(platform: .android)
// 导出
let result = try await tester.exportController.exportResources(
request: nil, progress: nil
)
#expect(result.exportProperties.uncompressedBytes > 0)
// 导入导出的数据
try await tester.importController.importResources(
from: result.extractedResourcesURL,
importRequest: nil, progress: nil
)
try await tester.importController.registerImportCompletion(with: .success)
}返回结果中的暴露、(不可压缩时为nil)、和属性。
DeviceToDeviceExportPropertiesuncompressedBytescompressedBytessizeEstimateversion更多测试模式请查看 references/appmigrationkit-patterns.md。
Common Mistakes
常见错误
DON'T: Catch cancellation errors from ResourcesArchiver
不要:捕获ResourcesArchiver抛出的取消错误
swift
// WRONG -- system kills the extension if cancellation is swallowed
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
do {
try await archiver.appendItem(at: fileURL)
} catch is CancellationError {
// Swallowing this causes termination
}
}
// CORRECT -- let cancellation propagate
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
try await archiver.appendItem(at: fileURL)
}swift
// 错误 -- 如果吞掉取消错误,系统会杀死扩展
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
do {
try await archiver.appendItem(at: fileURL)
} catch is CancellationError {
// 吞掉该错误会导致进程终止
}
}
// 正确 -- 让取消错误向上传播
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
try await archiver.appendItem(at: fileURL)
}DON'T: Leave long gaps between archiver append calls
不要:两次归档器append调用之间间隔过长
swift
// WRONG -- system may assume the extension is hung and terminate it
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
let allFiles = gatherAllFiles() // Takes 30 seconds
for file in allFiles {
try await archiver.appendItem(at: file)
}
}
// CORRECT -- interleave file preparation with archiving
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
for file in knownFilePaths() {
try await archiver.appendItem(at: file)
}
}swift
// 错误 -- 系统可能认为扩展已挂起并终止它
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
let allFiles = gatherAllFiles() // 耗时30秒
for file in allFiles {
try await archiver.appendItem(at: file)
}
}
// 正确 -- 文件准备和归档交错执行
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
for file in knownFilePaths() {
try await archiver.appendItem(at: file)
}
}DON'T: Convert files to intermediate format during export
不要:导出过程中将文件转换为中间格式
swift
// WRONG -- may exhaust disk space creating temporary copies
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
let converted = try convertToJSON(originalDatabase) // Doubles disk usage
try await archiver.appendItem(at: converted)
}
// CORRECT -- export files as-is, convert on import side if needed
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
try await archiver.appendItem(at: originalDatabase)
}swift
// 错误 -- 创建临时副本可能耗尽磁盘空间
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
let converted = try convertToJSON(originalDatabase) // 磁盘占用翻倍
try await archiver.appendItem(at: converted)
}
// 正确 -- 按原格式导出文件,如需转换在导入端执行
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
try await archiver.appendItem(at: originalDatabase)
}DON'T: Ignore app group containers during import error recovery
不要:导入错误恢复时忽略应用组容器
swift
// WRONG -- system clears app container but not app groups on error
func importResources(at url: URL, request: ResourcesImportRequest) async throws {
try writeToAppGroup(data)
try writeToAppContainer(data) // If this throws, app group has stale data
}
// CORRECT -- clear app group data before importing
func importResources(at url: URL, request: ResourcesImportRequest) async throws {
try clearAppGroupData()
try writeToAppGroup(data)
try writeToAppContainer(data)
}swift
// 错误 -- 出错时系统会清空应用容器,但不会清空应用组
func importResources(at url: URL, request: ResourcesImportRequest) async throws {
try writeToAppGroup(data)
try writeToAppContainer(data) // 如果这里抛出错误,应用组会残留旧数据
}
// 正确 -- 导入前先清空应用组数据
func importResources(at url: URL, request: ResourcesImportRequest) async throws {
try clearAppGroupData()
try writeToAppGroup(data)
try writeToAppContainer(data)
}DON'T: Forget to clear import status after handling it
不要:处理完迁移状态后忘记清空
swift
// WRONG -- migration UI shows every launch
if let status = MigrationStatus.importStatus {
showMigrationResult(status)
// Missing clearImportStatus()
}
// CORRECT
if let status = MigrationStatus.importStatus {
showMigrationResult(status)
MigrationStatus.clearImportStatus()
}swift
// 错误 -- 每次启动都会展示迁移UI
if let status = MigrationStatus.importStatus {
showMigrationResult(status)
// 缺少clearImportStatus()调用
}
// 正确
if let status = MigrationStatus.importStatus {
showMigrationResult(status)
MigrationStatus.clearImportStatus()
}Review Checklist
审核清单
- Extension target added with entitlement
com.apple.developer.app-migration.data-container-access - Entitlement array contains exactly one string: the containing app's bundle identifier
- Extension conforms to or
ResourcesExportingWithOptionsfor exportResourcesExporting - Extension conforms to for import
ResourcesImporting - returns a reasonable byte estimate
resourcesSizeEstimate - is set and will be checked on import for format compatibility
resourcesVersion - Export calls incrementally without long pauses
appendItem - Cancellation errors from are not caught
ResourcesArchiver - Import clears app group containers before writing new data
- Containing app checks on first launch
MigrationStatus.importStatus - called after handling the migration result
clearImportStatus() - used in unit tests to validate export and import
AppMigrationTester - Files are exported as-is without intermediate format conversion on the export side
- from import request used to handle versioned data formats
sourceVersion
- 已添加扩展target,并配置了权限
com.apple.developer.app-migration.data-container-access - 权限数组仅包含一个字符串:宿主应用的包ID
- 导出功能已遵循或
ResourcesExportingWithOptions协议ResourcesExporting - 导入功能已遵循协议
ResourcesImporting - 返回合理的字节预估
resourcesSizeEstimate - 已设置,导入时会检查该值保证格式兼容性
resourcesVersion - 导出时增量调用,无长时间停顿
appendItem - 没有捕获抛出的取消错误
ResourcesArchiver - 导入时写入新数据前已清空应用组容器
- 宿主应用首次启动时会检查
MigrationStatus.importStatus - 处理完迁移结果后调用了
clearImportStatus() - 单元测试中使用验证导出和导入逻辑
AppMigrationTester - 导出端按原格式导出文件,未做中间格式转换
- 导入时使用请求中的处理不同版本的数据格式
sourceVersion
References
参考资料
- Extended patterns (combined extension, versioned migration, file enumeration, error recovery): references/appmigrationkit-patterns.md
- AppMigrationKit framework
- AppMigrationExtension
- ResourcesExportingWithOptions
- ResourcesImporting
- ResourcesArchiver
- MigrationStatus
- MigrationDataContainer
- AppMigrationTester
- Data container entitlement
- 扩展模式(组合扩展、版本化迁移、文件枚举、错误恢复):references/appmigrationkit-patterns.md
- AppMigrationKit框架
- AppMigrationExtension
- ResourcesExportingWithOptions
- ResourcesImporting
- ResourcesArchiver
- MigrationStatus
- MigrationDataContainer
- AppMigrationTester
- 数据容器权限