Last active
November 12, 2025 15:47
-
-
Save tprochazka/e1966db92a832f37f2ebfd227e35ee28 to your computer and use it in GitHub Desktop.
DataStore migration function from one DataStore to the new one with possibility to change key names
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Creates a DataStore-to-DataStore migration that optionally remaps keys from old names to new names. | |
| * | |
| * Useful when migrating from a legacy DataStore to a new unified DataStore. | |
| * Unlike [SharedPreferencesMigration], this can read from another DataStore (Protocol Buffer format). | |
| * | |
| * This migration creates a temporary DataStore instance to read the legacy data, then closes it | |
| * automatically after migration completes. This prevents keeping unused DataStore instances open. | |
| * | |
| * @param context The application context | |
| * @param legacyDataStoreName Name of the legacy DataStore file (without path or extension) | |
| * @param keyMapping Map of old key names to new key names (oldKey -> newKey). | |
| * If oldKey == newKey, the key is copied as-is. | |
| * | |
| * Example: | |
| * ``` | |
| * DataStoreMigration( | |
| * context = context, | |
| * legacyDataStoreName = "dataStorePref.AppLaunchCount", | |
| * keyMapping = mapOf( | |
| * "app_launch_count" to "app_launch_count", // No rename | |
| * "old_key_name" to "new_key_name" // With rename | |
| * ) | |
| * ) | |
| * ``` | |
| */ | |
| @Suppress("FunctionName", "SameParameterValue") | |
| private fun DataStoreMigration( | |
| context: Context, | |
| legacyDataStoreName: String, | |
| keyMapping: Map<String, String> | |
| ): DataMigration<Preferences> { | |
| return object : DataMigration<Preferences> { | |
| override suspend fun shouldMigrate(currentData: Preferences): Boolean { | |
| // Check if legacy DataStore file exists | |
| val legacyFile = File(context.filesDir, "datastore/$legacyDataStoreName.preferences_pb") | |
| if (!legacyFile.exists()) { | |
| return false | |
| } | |
| // Migrate if any of the new keys don't exist yet | |
| return keyMapping.values.any { newKey -> | |
| // Check all possible types - if none exist, we should migrate | |
| currentData[intPreferencesKey(newKey)] == null && | |
| currentData[longPreferencesKey(newKey)] == null && | |
| currentData[stringPreferencesKey(newKey)] == null && | |
| currentData[booleanPreferencesKey(newKey)] == null && | |
| currentData[floatPreferencesKey(newKey)] == null | |
| } | |
| } | |
| override suspend fun migrate(currentData: Preferences): Preferences { | |
| // Create a temporary DataStore to read legacy data | |
| val legacyDataStore = PreferenceDataStoreFactory.create( | |
| produceFile = { context.preferencesDataStoreFile(legacyDataStoreName) } | |
| ) | |
| val legacyData = legacyDataStore.data.first() | |
| return currentData.toMutablePreferences().apply { | |
| keyMapping.forEach { (oldKey, newKey) -> | |
| // Try each type - DataStore stores typed keys | |
| legacyData[intPreferencesKey(oldKey)]?.let { | |
| this[intPreferencesKey(newKey)] = it | |
| } | |
| legacyData[longPreferencesKey(oldKey)]?.let { | |
| this[longPreferencesKey(newKey)] = it | |
| } | |
| legacyData[stringPreferencesKey(oldKey)]?.let { | |
| this[stringPreferencesKey(newKey)] = it | |
| } | |
| legacyData[booleanPreferencesKey(oldKey)]?.let { | |
| this[booleanPreferencesKey(newKey)] = it | |
| } | |
| legacyData[floatPreferencesKey(oldKey)]?.let { | |
| this[floatPreferencesKey(newKey)] = it | |
| } | |
| legacyData[stringSetPreferencesKey(oldKey)]?.let { | |
| this[stringSetPreferencesKey(newKey)] = it | |
| } | |
| } | |
| }.toPreferences() | |
| // Note: The temporary DataStore will be garbage collected after migration | |
| } | |
| override suspend fun cleanUp() { | |
| // We leave the original DataStore intact for safety | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment