How to save imported JSON file with Expo Filesystem

Viewed 3048

I have been working on a React Native project with Expo that uses a json file to store local data. I am importing the data like so import data from '../database.json'

I am making changes (adding and removing) to the imported JSON by using data.push(new_data). These changes are not persistent when I close the app because I cannot figure out how to save them. I have looked at using the expo-file-system library as so:

import * as FileSystem from 'expo-file-system';
...
FileSystem.writeAsStringAsync(FileSystem.documentDirectory + 'database.json', data);

This is from looking at examples in the API documentations. This however always throws promise rejections and doesn't end up writing the file. Can you point me in the right direction?

Also, should I import the database.json in a different way so I will already have the uri to save it to?

5 Answers

Use AsyncStorage instead. The react native package is deprecated but working, or use @react-native-community/async-storage and convert json to string (AsyncStorage can only store strings)

Set item

import AsyncStorage from '@react-native-community/async-storage';
...
await AsyncStorage.setItem('myData', JSON.stringify(data))

Get item

const data = await AsyncStorage.getItem('myData')

The documentation doesn't give an example of it's returned props in promises, so I was overlooking it for longer than I care to admit . I was really dedicated to figuring this out so I could use the Expo solution, and totally missed the return Promise for createFileAsync, so hopefully this saves someone a significant amount of time in the future.

import * as FileSystem from 'expo-file-system';
const { StorageAccessFramework } = FileSystem;

const saveFile = async () => {
  const permissions = await StorageAccessFramework.requestDirectoryPermissionsAsync();
  // Check if permission granted
  if (permissions.granted) {
    // Get the directory uri that was approved
    let directoryUri = permissions.directoryUri;
    let data = "Hello World";
    // Create file and pass it's SAF URI
    await StorageAccessFramework.createFileAsync(directoryUri, "filename", "application/json").then(async(fileUri) => {
      // Save data to newly created file
      await FileSystem.writeAsStringAsync(fileUri, data, { encoding: FileSystem.EncodingType.UTF8 });
    })
    .catch((e) => {
      console.log(e);
    });
  } else {
    alert("You must allow permission to save.")
  }
}

If you using AsyncStorage, it only store for small data. Maybe 6mb or 10 mb.

You can use expo fileSystem

import * as FileSystem from 'expo-file-system';
...
FileSystem.writeAsStringAsync(FileSystem.documentDirectory + 'database.json', data);

Convert your data (Type json to string) Such as this:

  writeData = async () => {
          var persons = ''
          await axios.get(`http://192.168.0.48:4000/api/sql/student`)
          .then(res => {
            persons = res.data
          })
          await FileSystem.writeAsStringAsync(FileSystem.documentDirectory + `offline_queue_stored.json`, JSON.stringify(persons));
        }

I found @JayMax answer very helpful however it's only for Android. On iOS all you need to do is use Sharing.shareAsync and then you can save data to the file. Check this example:

const fileUri = FileSystem.documentDirectory + 'data.txt';

FileSystem.writeAsStringAsync(fileUri, 'here goes your data from JSON. You can stringify it :)', {
  encoding: FileSystem.EncodingType.UTF8,
});

const UTI = 'public.text';

Sharing.shareAsync(fileUri, {UTI}).catch((error) => {
  console.log(error);
});

#1.If the JSON File is in your Project Folder (PC/Laptop)

import data from './database.json';

#2. If the JSON File is in your Phone

import * as FileSystem from 'expo-file-system';
import * as DocumentPicker from 'expo-document-picker';

this.state = {
        fileURI: null,
    };

componentDidMount = () =>{
    this._pickDocument();
}
_pickDocument = async () => {
    let result = await DocumentPicker.getDocumentAsync({});
    this.setState({
        fileURI: result.uri
    })
    let fileData = await FileSystem.readAsStringAsync(this.state.fileURI)
    console.log(fileData)
};
Related