30

A few reasons why I might do this:

  • To create some webviews and inject javascript loaded from files
  • To separate large chunks of text into separate files rather than forcing them into views
  • To include raw data of an arbitrary format (eg, CSV) to be used in the app

In React Native you can use require to import an image file, but as far as I've seen, this only works for image files. And it (strangely) also works for JSON files (see Importing Text from local json file in React native). However, I haven't seen anywhere talking about importing plain old text files.

Community
  • 1
  • 1
eremzeit
  • 4,055
  • 3
  • 30
  • 32

4 Answers4

11

After looking and asking around, the best I can come up with is to use a fork of the react-native-fs library to access android "assets". This fork is a pull request out and as soon as it gets merged you can use it.

Note that in android dev, "assets" specifically refer to accessing the raw contents of a file. In order to do this sort of thing on the react native side, you need to write a native module to interface with react, hence the library above. See here (and search for assets).

In your react native project, make a file called something like android/app/src/main/assets/text.txt. Using the version of react-native-fs mentioned above, you do:

RNFS.readFileAssets('test.txt').then((res) => {
  console.log('read file res: ', res);
})

update: If you want the pull request that would enable this ability to go through, you should go let the author know you want it by giving it a thumbs up on github.

eremzeit
  • 4,055
  • 3
  • 30
  • 32
  • 2
    As the author of that PR I support this solution! (Hopefully the module owner will integrate it soon) – Ben Clayton Aug 08 '16 at 19:09
  • 8
    What about iOS? – br4nnigan Feb 28 '17 at 11:32
  • Its so sad that the only working solution to use dynamic and static resources is not working for all targeted environments, which is the reason one is using react-native in the first place. – Macilias Nov 21 '19 at 12:59
  • This worked for me on Android. Where would you keep the 'test.txt' in iOS and do we use the same function to read it ? – sushrut619 Feb 06 '20 at 16:10
  • @br4nnigan in iOS create folder assets in the main bundle same root of ( libraries, pods), add the files in there then use readFile but the path should include the MainBundlePath for instance : RNFS.readFile(`${RNFS.MainBundlePath}/${file}.txt`).then(result => { this.setState({content:result}) }).catch(err => { console.log(err); }); – Basil Satti Jun 15 '20 at 18:40
  • @BasilSatti I can't seem to get this working, have you got an example of this? – rose specs Sep 10 '20 at 13:52
  • 1
    @witacur create folder assets inside the ios folder of your react-native project, here is a simple example I've made (https://github.com/baselka/kindleapp/blob/master/App.js) . Notice the readFile function and the assets folder inside the ios folder which contains the files – Basil Satti Sep 12 '20 at 15:27
6

There is a library that solves this exact problem: React-Native-Local-Resource. The library allows you to asynchronously load any type of text file in Android and iOS at runtime.

Scott Storch
  • 794
  • 3
  • 9
  • 16
1

Here is a simple react-native project example of implementing FS and read files for both IOS and Android .

https://github.com/baselka/kindleapp

(Implement a simple application to render 2 files (book file) in an android and iOS application on a tablet. The app must emulate the functionality where you are reading a book file in your app like in a kindle app)

Basil Satti
  • 700
  • 1
  • 9
  • 24
0

Here's how I did it synchronously in Swift and JS for iOS/tvOS/macOS, based on the React Native docs: Exporting Constants.

  • Disadvantage: Note that the file will be loaded into memory once upon startup, and won't be dynamically re-loadable.

  • Advantage: It's synchronous, simple, and works at run-time whether in native or JS.

MyJSFile.js

import { NativeModules } from "react-native";

console.log(NativeModules.MyNativeModule.MyFileContents);

We import our native module and access the MyFileContents constant that we expose upon it. It works synchronously with no bridge-crossing (as far as I understand, it's injected into the React Native JSContext via JavaScriptCore).

In Build Phases, ensure that this file is added into Copy Bundle Resources. Otherwise your app will quickly crash upon trying to read it.

MyNativeModule.swift

import Foundation

@objc(MyNativeModule)
class MyNativeModule: RCTEventEmitter {
    @objc override func constantsToExport() -> [AnyHashable : Any]! {
        let contents: String = try! String(contentsOfFile: Bundle.main.path(forResource: "MyFile.min", ofType: "js")!)
        return [
            "MyFileContents": contents
        ]
    }
    @objc override func supportedEvents() -> [String]! {
        return []
    }
    @objc override static func requiresMainQueueSetup() -> Bool {
        return false
    }
}

One can likely make a simpler/slimmer native module than this one (by subclassing something with less functionality than RCTEventEmitter), but this is the file I had lying around to work with, so here it is.

MyProject-Bridging-Header.h

#import <React/RCTBridge.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTUIManager.h>
#import <React/RCTEventEmitter.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTJavaScriptLoader.h>
#import <React/RCTLinkingManager.h>
#import <React/RCTRootView.h>
#import <React/RCTEventDispatcher.h>

Here's the bridging header I'm using. It exposes a lot more headers than are strictly necessary, but you may need them for other native modules later anyway.

As this approach uses Swift, make sure to enter your Build Settings and set Always Embed Swift Standard Libraries to Yes if you haven't already. And if this is the first time building your app with Swift embedded, you may want to clear DerivedData for luck before building.

... Or simply rewrite that same Swift code in Obj-C, as the documentation shows how.

Jamie Birch
  • 5,839
  • 1
  • 46
  • 60