Created
October 8, 2019 12:12
-
-
Save ericluong/8176413b6f02bdc97b97dc0f92aa1f69 to your computer and use it in GitHub Desktop.
Migrating AsyncStorage after ejecting from Expo (SDK 34)
This file contains 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
package com.company.project; | |
import java.io.File; | |
import java.net.URLEncoder; | |
import android.content.ContentValues; | |
import android.content.Context; | |
import android.database.Cursor; | |
import android.database.sqlite.SQLiteDatabase; | |
import android.util.Log; | |
import com.facebook.react.modules.storage.ReactDatabaseSupplier; | |
public class AsyncStorageMigration { | |
static final String LOG_TAG = "expo_storage_migration"; | |
static final String PERSIST_KEY = "persist:root"; | |
static final String TABLE_CATALYST = "catalystLocalStorage"; | |
static final String KEY_COLUMN = "key"; | |
static final String VALUE_COLUMN = "value"; | |
private static Context mContext; | |
private static String expoDatabaseName; | |
public static void migrate(Context context) { | |
mContext = context; | |
expoDatabaseName = getExpoDatabaseName(); | |
if (expoDatabaseName.length() == 0) | |
return; | |
Boolean expoDatabaseExists = checkExpoDatabase(); | |
if (!expoDatabaseExists) { | |
Log.v(LOG_TAG, "Expo AsyncStorage was previously migrated. Exiting migration..."); | |
return; | |
} | |
String data = getOldData(); | |
if (data.length() == 0) { | |
Log.v(LOG_TAG, "Could not get old data. Exiting migration..."); | |
return; | |
} | |
if (!deleteOldDatabase()) { | |
Log.v(LOG_TAG, "Could not delete old database. Exiting migration..."); | |
return; | |
} | |
populateNewStore(data); | |
Log.v(LOG_TAG, "Migration done!"); | |
} | |
private static String getExpoDatabaseName() { | |
String databaseName = ""; | |
String experienceId = getExperienceId(); | |
if (experienceId == null || experienceId.length() == 0) { | |
Log.v(LOG_TAG, "No Experience ID. Exiting migration..."); | |
} else { | |
try { | |
String experienceIdEncoded = URLEncoder.encode(experienceId, "UTF-8"); | |
databaseName = "RKStorage-scoped-experience-" + experienceIdEncoded; | |
} catch (Exception e) { | |
Log.e(LOG_TAG, "Could not get Expo database name"); | |
} | |
} | |
return databaseName; | |
} | |
private static String getExperienceId() { | |
return BuildConfig.LEGACY_EXPO_EXPERIENCE_ID; | |
} | |
private static boolean checkExpoDatabase() { | |
File dbFile = mContext.getDatabasePath(expoDatabaseName); | |
return dbFile.exists(); | |
} | |
private static String getOldData() { | |
String data = ""; | |
SQLiteDatabase readableDatabase = null; | |
Cursor cursor = null; | |
try { | |
String databasePath = mContext.getDatabasePath(expoDatabaseName).getPath(); | |
readableDatabase = SQLiteDatabase.openDatabase(databasePath, null, SQLiteDatabase.OPEN_READONLY); | |
cursor = readableDatabase.query(TABLE_CATALYST, new String[]{VALUE_COLUMN}, KEY_COLUMN + " = ?", new String[]{PERSIST_KEY}, null, null, null); | |
if (cursor != null && cursor.moveToFirst()) { | |
data = cursor.getString(cursor.getColumnIndex(VALUE_COLUMN)); | |
} | |
} catch (Exception e) { | |
Log.e(LOG_TAG, "Get old data error: " + e.getMessage()); | |
} finally { | |
if (readableDatabase != null) { | |
readableDatabase.close(); | |
} | |
} | |
return data; | |
} | |
private static Boolean deleteOldDatabase() { | |
return mContext.deleteDatabase(expoDatabaseName); | |
} | |
private static void populateNewStore(String value) { | |
SQLiteDatabase readableDatabase = null; | |
try { | |
readableDatabase = ReactDatabaseSupplier.getInstance(mContext).getReadableDatabase(); | |
// Check if entry exists | |
Cursor cursor = readableDatabase.query(TABLE_CATALYST, new String[]{VALUE_COLUMN}, KEY_COLUMN + " = ?", new String[]{PERSIST_KEY}, null, null, null); | |
boolean entryExists = cursor.getCount() != 0; | |
cursor.close(); | |
ContentValues args = new ContentValues(); | |
args.put(KEY_COLUMN, PERSIST_KEY); | |
args.put(VALUE_COLUMN, value); | |
// Update value | |
if (entryExists) { | |
Log.v(LOG_TAG, "Entry already exists, ready for update..."); | |
readableDatabase.update(TABLE_CATALYST, args, KEY_COLUMN + " = ?", new String[]{PERSIST_KEY}); | |
} | |
// Insert value | |
else { | |
Log.v(LOG_TAG, "Entry does not exists, ready for insertion..."); | |
readableDatabase.insert(TABLE_CATALYST, null, args); | |
} | |
} catch (Exception e) { | |
Log.e(LOG_TAG, "Populate error: " + e.getMessage()); | |
} finally { | |
if (readableDatabase != null) { | |
readableDatabase.close(); | |
} | |
} | |
} | |
} |
Hi @ericluong, thanks for providing this solution. Can you say where did you put AsyncStorageMigration.java file in android folder?
Hello @yaroslavnikiforov, this file is located next to the MainApplication.java and MainActivity.java files in android/app/src/main/java/project/path
Thanks, @ericluong, this solution works great
@yaroslavnikiforov You're welcome! Migrating out of Expo was painful (especially with an app in production) but it has been worth it
Thank you for the solution, if you are not using Redux this can be useful too: https://gist.github.com/luizjr92/db091719f09617abc5909ba64485e395
Guys, I just want to say thank you for this!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is how I migrated my AsyncStorage data on Android after ejecting from Expo 😁
If your Experience ID is @project-owner/project, Expo database name will be RKStorage-scoped-experience-%40project-owner%2Fproject.
Because I'm using Redux Persist on my project, I only need to copy the entry with the
persist:root
key from Expo database to the React Native one.MainApplication.java
@Override public void onCreate() { super.onCreate(); + AsyncStorageMigration.migrate(getApplicationContext()); SoLoader.init(this, /* native exopackage */ false); }
build.gradle
buildTypes { debug { ... + buildConfigField "String", "LEGACY_EXPO_EXPERIENCE_ID", "\"@project-owner/project\"" } }