Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate AsyncStorage to ReactNative (iOS), losing old data #5651

Closed
charlyBerthet opened this issue Sep 15, 2019 · 9 comments
Closed

Migrate AsyncStorage to ReactNative (iOS), losing old data #5651

charlyBerthet opened this issue Sep 15, 2019 · 9 comments

Comments

@charlyBerthet
Copy link

🐛 Bug Report

I wanted to migrate from ejected Expo to the last react native version and figured out expo do not use the same directory path as ReactNative Core so migration from Expo to React Native Community result in a lost of my local stored data.

Here Expo directory name (@"RCTAsyncLocalStorage"):

NSString *localStorageDirectory = [fileSystemModule.documentDirectory stringByAppendingPathComponent:EX_UNVERSIONED(@"RCTAsyncLocalStorage")];

Here ReactNative Core directory name (@"RCTAsyncLocalStorage_V1"):
https://github.com/facebook/react-native/blob/ce0ad9f7121c70b1dd27508910156ecc95b6f10c/React/Modules/RCTAsyncLocalStorage.m#L20

Here ReactNative Community migration PR:
react-native-async-storage/async-storage@8d275ad#diff-54c57f4041d9a698ab027d21344b6e63

Why do you use @"RCTAsyncLocalStorage" instead of @"RCTAsyncLocalStorage_V1"?

Environment

Steps to Reproduce

Expected Behavior

Do not lose local stored data once migrated to https://github.com/react-native-community/async-storage.

Actual Behavior

Keep local stored data once migrated to https://github.com/react-native-community/async-storage.

Reproducible Demo

@jaquan-paik
Copy link

same issue here

@burivuhster
Copy link

burivuhster commented Oct 16, 2019

Edit: Updated the source code to handle AsyncStorage migration properly
Edit 2: Added the note about Android

Hey guys, just in case any of you are still looking for a solution.

I faced the same issue when I was rewriting my app from ExpoKit (SDK 31) to the bare Expo workflow and SDK 35.
I solve it by accessing an old documentDirectory via FileSystem API and copying all the content with manual migration code.

Here is a piece of code to give you an idea:

import * as FileSystem from 'expo-file-system';
import AsyncStorage from '@react-native-community/async-storage';

const oldDocumentDirectory =
    FileSystem.documentDirectory +
    'ExponentExperienceData/' +
    encodeURIComponent('%40burivuh%2Fmy-app-expo-slug') +
    '/';

export async function migrate() {
    const oldDocumentDirectoryContents = await FileSystem.readDirectoryAsync(
        oldDocumentDirectory
    );

    for(let itemUri of oldDocumentDirectoryContents) {
        let result = await FileSystem.moveAsync({
            from: oldDocumentDirectory + itemUri,
            to: FileSystem.documentDirectory + itemUri
        });
    }

    // Migrate AsyncStorage
    // old AsyncStorage (from Expo) was using directory "RCTAsyncLocalStorage" under documentDirectory
    // new AsyncStorage (from @react-native-community/async-storage) uses "RCTAsyncLocalStorage_V1"
    const oldAsyncStorageInfo = await FileSystem.getInfoAsync(
        FileSystem.documentDirectory + 'RCTAsyncLocalStorage/manifest.json'
    );
    if (oldAsyncStorageInfo && oldAsyncStorageInfo.exists) {
        const oldAsyncStorageContents = await FileSystem.readAsStringAsync(FileSystem.documentDirectory + 'RCTAsyncLocalStorage/manifest.json');
        const oldAsyncStorageObj = JSON.parse(oldAsyncStorageContents);

        if(oldAsyncStorageObj) {
            // Data entries may be placed inline as a property in the manifest.json file
            // OR
            // for values with length more than 1024 it will be placed in a separate file with the name equal md5(key)
            if (oldAsyncStorageObj.settings) {
                await AsyncStorage.setItem('settings', oldAsyncStorageObj.settings);
            } else if(oldAsyncStorageObj.settings === null) {
                const settingsFilename = "2e5d8aa3dfa8ef34ca5131d20f9dad51"; // md5 hash of key "settings"
                const oldSettingsFileContents = await FileSystem.readAsStringAsync(FileSystem.documentDirectory + 'RCTAsyncLocalStorage/' + settingsFilename);

                await AsyncStorage.setItem('settings', oldSettingsFileContents);
            }
        }
    }

    // Save a flag in AsyncStorage that migration was completed
    await AsyncStorage.setItem('migration-from-expo-31-completed', true);

    console.log('Migration from Expo 31 completed');
    return true;
}

Disclaimer: please review it carefully and test it first and use it with caution.
Disclaimer 2: It's only for the iOS version, I didn't check AsyncStorage on Android at all

@solace
Copy link

solace commented Oct 22, 2019

@burivuhster Hi, out of interest, have you tried this in TestFlight?

@burivuhster
Copy link

@solace Yes, and the update with data migration code is in production since yesterday. But I enabled phased release, so only a couple of real users were updated to this moment. So far so good.
I discovered that AsyncStorage is a bit more complicated than I expected, I'll edit the code above.

@solace
Copy link

solace commented Oct 23, 2019

@burivuhster Good to know, thanks. I'm trying to upgrade from SDK 32 to bare and I'm finding the data is lost when I attempt to inspect the ExponentExperienceData manifest file. It works fine in the simulator but on TestFlight there is no data. Did you follow a specific guide, or do you have steps for transitioning to bare? I feel like I'm missing something critical here that's causing the data to not be there. Switching back down to an older version shows the old data, so it's still there, just not accessible by the bare version.

@burivuhster
Copy link

@solace I didn't find any guides, that's why I decided to share my experience here. Please note that AsyncStorage saves values in separate files when the value length is more than 1024 bytes. And it keeps a null value in manifest.json in this case.

@solace
Copy link

solace commented Oct 25, 2019

@burivuhster That note about AsyncStorage saving values in different files was the key to my issue. Thanks!

@github-actions
Copy link
Contributor

This issue is stale because it has been open for 60 days with no activity. If there is no activity in the next 7 days, the issue will be closed.

@github-actions github-actions bot added the stale label Jan 18, 2022
@github-actions
Copy link
Contributor

This issue was closed because it has been inactive for 7 days since being marked as stale. Please open a new issue if you believe you are encountering a related problem.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 22, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants