# Simplifying File Download and Viewing in React Native Apps

> Originally written in 2024. Content may vary slightly across newer versions.

#### What is React Native, and how does it facilitate cross-platform development?

React Native enables developers to build mobile applications for both iOS and Android platforms using a single codebase.

#### Why is file management important in React Native development?

File management is crucial for many applications, such as handling downloads and opening files. Implementing these features requires consideration of platform-specific differences and permissions.

#### What tools or library should be used?

When it comes to file operations, the `react-native-fetch-blob` library offers robust capabilities. It provides a comprehensive set of functions for handling file downloads, uploads, and other file-related tasks in React Native applications.

#### File Download

Key considerations:

*   Choose an appropriate directory based on the platform.
    
*   Construct the file path using a unique identifier.
    
*   Execute platform-specific download logic, handling permissions and errors accordingly.
    

```javascript
import { PermissionAndroid, Platform, Alert } from "react-native";
import DeviceInfo from "react-native-device-info";
import ReactNativeBlobUtil from "react-native-blob-util";
import FileViewer from "react-native-file-viewer";
import _ from "lodash";

const downloadPdfToFileSystem = async (base64, fileId) => {
  const IS_IOS = Platform.OS === "ios";
  const {
    dirs: { DownloadDir, DocumentDir },
  } = ReactNativeBlobUtil.fs;
  const dirs = IS_IOS ? DocumentDir : DownloadDir;
  const path = dirs + '/' + fileId + '.pdf';

  try {
    if (!IS_IOS) {
      const systemVersion = DeviceInfo.getSystemVersion();
      const isDownloadAllowed = await PermissionAndroid.request(
        PermissionAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
      );

      if (
        systemVersion >= "11.0" ||
        isDownloadAllowed === PermissionAndroid.RESULTS.GRANTED
      ) {
        await ReactNativeBlobUtil.fs.writeFile(path, base64, "base64");
      }

      return path;
    }

    const ReactNativeBlobConfigs = { fileCache: true, path };
    const fileUrl = 'data:application/pdf;base64,' + base64;
    const response = await ReactNativeBlobUtil.config(
      ReactNativeBlobConfigs
    ).fetch("GET", fileUrl, {});

    return response.path();
  } catch (e) {
    console.log("PDF error ", e);
    Alert.alert(e.toString());
  }
};
```

To streamline file downloads, I’ve implemented a function called `downloadPdfToFileSystem`, which facilitates downloading PDF data in base64 format and storing it in the device’s file system.

Here’s a breakdown of the process:

1\. Choose an appropriate directory based on the platform: Use `DocumentDir` for iOS and `DownloadDir` for Android.

2\. Construct the file path using a unique identifier, `fileId`.

3\. Execute platform-specific download logic:

**For Android**: Check the device’s system version. If it’s 11.0 or higher, no need to request WRITE\_EXTERNAL\_STORAGE permission; proceed with file writing. Otherwise, request permission using `PermissionAndroid.request`.

**For iOS:** Configure file cache and path using `ReactNativeBlobUtil.config`; Use the `fetch` method to retrieve the PDF file from base64 data and save it.

4\. Handle errors gracefully: Log error messages to the console and display an alert dialog if any issues arise.

☀️ Highlights:

*   **Choice of Directories:**
    

`Documents` directory in iOS and `Downloads` in Android: Selected based on platform differences, permission management, and user experience considerations.

**Platform Differences:** iOS and Android platforms have different file system structures. iOS apps typically save user-generated files in the `Documents` directory, while Android apps tend to save downloadable files in the external storage’s `Downloads` directory.

**Permission Management:** Accessing external storage on Android usually requires dynamically requesting permissions, especially after Android 11 (API level 30), which imposes stricter restrictions on file access. In contrast, iOS is relatively more lenient in terms of file access permissions and does not require explicit permission requests.

**User Experience:** Saving downloaded files in familiar locations can enhance user experience. For example, on Android devices, saving files to the `Downloads` directory allows users to easily locate downloaded files in the system file manager or other apps.

*   **Understanding** `PermissionAndroid.request`
    

This function requests write access to external storage on Android. It’s an asynchronous operation, awaiting the user’s response, which can be granted, denied, or permanently denied (“never ask again”).

Based on the user’s response, developers can take appropriate actions, such as proceeding with the file writing operation or providing relevant prompts when the user denies the permission.

*   **Data URL Explanation**
    

A data URL is a special format containing Base64-encoded data. In this case, it specifies the MIME type (“application/pdf”) and the encoded PDF content, guiding `ReactNativeBlobUtil` on data handling.

*   **Handling Image Downloads**
    

For images, modify the MIME type to “image/jpeg” or “image/png” and replace “base64” with image data. This ensures proper handling by `ReactNativeBlobUtil`.

#### Check File Existence

```javascript
const checkFileExistence = async (fileId) => {
  const IS_IOS = Platform.OS === "ios";
  const {
    dirs: { DownloadDir, DocumentDir },
  } = ReactNativeBlobUtil.fs;
  const dirs = IS_IOS ? DocumentDir : DownloadDir;
  const path = dirs + '/' + fileId + '.pdf';

  try {
    const isExist = await ReactNativeBlobUtil.fs.exists(path);
    return { isExist, path };
  } catch (e) {
    console.log("check file existence error ", e);
    Alert.alert(e.toString());
  }
};
```

This function checks the existence of a file by constructing the file path based on the provided `fileId`.

It uses the `ReactNativeBlobUtil.fs.exists` method to check if the file exists at the specified path. The result is stored in the `isExist` variable.

If any errors occur during the process, such as invalid file paths or permission issues, they are caught in the`catch` block. The error message is logged to the console using `console.log`and displayed as an alert using `Alert.alert`.

#### Opening Downloaded Files

```javascript
const openExistingFile = async (path, isInModalOrAlerts = false) => {
  const IS_IOS = Platform.OS === "ios";

  try {
    if (!IS_IOS) {
      return await ReactNativeBlobUtil.android.actionViewIntent(
        path,
        "application/pdf"
      );
    }

    // ReactNativeBlobUtil.ios.openDocument does not work with Modal and Alerts components
    // Reference: https://github.com/joltup/rn-fetch-blob/issues/243
    if (isInModalOrAlerts) {
      await FileViewer.open(path);
    } else {
      await ReactNativeBlobUtil.ios.previewDocument(path);
    }
  } catch (e) {
    console.log("open file error: ", e);
    Alert.alert(e.toString());
  }
};
```

On the Android platform, the `ReactNativeBlobUtil.android.actionViewIntent` method is employed to open the file. This method sends an action view intent, prompting the system to open the specified file.

On iOS, two different methods are used:

1\. If the `isInModalOrAlerts` parameter is true, indicating that the current application context is within a modal dialog or alert, the `FileViewer.open` method is invoked to open the file. This is because the `ReactNativeBlobUtil.ios.previewDocument` method *may not function properly within modal dialogs or alerts* due to certain limitations.

2\. If the `isInModalOrAlerts` parameter is false, indicating that the current application context is not within a modal dialog or alert, the `ReactNativeBlobUtil.ios.previewDocument` method is directly called to open the file.

In case of any errors, the error messages are logged using `console.log` and displayed using the `Alert.alert` method.

By following these guidelines, developers can simplify file download and viewing in React Native apps, ensuring smooth user experiences across iOS and Android platforms.
