BindingMigration

public class BindingMigration

Manages the migration of device binding data from the legacy SDK to the new SDK format.

This class orchestrates a multi-step migration process that transfers user key metadata from the legacy keychain location to the new storage format.

Migration Steps

The migration executes three sequential steps:

  1. Check for Legacy Data: Verifies if legacy keychain data exists and needs migration
  2. Migrate User Keys: Transfers user key metadata from legacy keychain to new storage
  3. Cleanup: Removes legacy keychain data after successful migration

Usage

Automatic migration (recommended):

// Migration starts automatically when BindingModule is first used
// or can be triggered explicitly
Task {
    try await BindingMigration.migrate()
}

Manual migration with custom configuration:

Task {
    try await BindingMigration.migrate(
        accessGroup: "com.myapp.keychain",
        logger: myLogger,
        cleanupLegacyData: true
    )
}

Progress Monitoring

The migration logs progress updates that can be observed through the provided logger:

  • Migration started
  • Legacy data check results
  • Number of keys migrated
  • Cleanup status
  • Completion or error details
  • Executes the device binding migration process.

    This method migrates user key metadata from the legacy keychain location (com.forgerock.ios.devicebinding.keychainservice) to the new storage format. The migration is idempotent - running it multiple times is safe and will not duplicate or corrupt data.

    The method performs the following steps:

    1. Checks if migration has already been completed
    2. Verifies if legacy data exists in the keychain
    3. Reads all user keys from the legacy storage
    4. Saves them to the new storage format
    5. Optionally deletes the legacy data

    Throws

    MigrationError if migration fails at any step.

    Example

    do {
        try await BindingMigration.migrate(
            accessGroup: "com.myapp.shared",
            logger: Logger.standard
        )
        print("Migration completed successfully")
    } catch MigrationError.noLegacyDataFound {
        print("No legacy data to migrate")
    } catch {
        print("Migration failed: \(error)")
    }
    

    Declaration

    Swift

    public static func migrate(
        accessGroup: String? = nil,
        logger: Logger? = nil,
        cleanupLegacyData: Bool = true,
        storageConfig: UserKeyStorageConfig? = nil
    ) async throws

    Parameters

    accessGroup

    The keychain access group that was configured in the legacy app, if any. Defaults to nil (no access group).

    logger

    Optional logger for debugging and monitoring migration progress.

    cleanupLegacyData

    Whether to delete legacy keychain data after successful migration. Defaults to true.

    storageConfig

    Custom storage configuration for the new format. If not provided, uses the default UserKeyStorageConfig.

  • Checks if migration is needed without performing it.

    This method checks if legacy data exists in the keychain without attempting migration. It can be used to determine if migration will be necessary.

    Declaration

    Swift

    public static func isMigrationNeeded(
        accessGroup: String? = nil,
        logger: Logger? = nil
    ) async -> Bool

    Parameters

    accessGroup

    The keychain access group that was configured in the legacy app, if any.

    logger

    Optional logger for debugging.

    Return Value

    true if legacy data exists and migration is needed, false otherwise.

  • Attempts to migrate legacy data if it exists and hasn’t been migrated yet.

    This is a convenience method that silently handles the case where no legacy data exists. It’s designed to be called before bind or sign operations.

    Declaration

    Swift

    public static func migrateIfNeeded(
        accessGroup: String? = nil,
        logger: Logger? = nil,
        cleanupLegacyData: Bool = true
    ) async -> Bool

    Parameters

    accessGroup

    The keychain access group that was configured in the legacy app, if any.

    logger

    Optional logger for debugging and monitoring migration progress.

    cleanupLegacyData

    Whether to delete legacy keychain data after successful migration.

    Return Value

    true if migration was performed, false if no migration was needed.

  • resetMigrationState() Asynchronous

    Resets the migration attempted flag for testing purposes.

    Warning

    This should only be used in test environments.

    Declaration

    Swift

    public static func resetMigrationState() async