Created PouchDB@7 for React Native
Tags: pouchdb, reactnative
Published: 2021-02-12 06:47:06

[About 2 years ago](https://dev.to/craftzdog/hacking-pouchdb-to-use-on-react-native-1gjh), I hacked [PouchDB](https://pouchdb.com/)@6 to make it able to run on React Native with attachments support. But the current latest version is 7 now.
Unfortunately, the PouchDB community is not interested in supporting React Native.
So, we have to hack it again for getting v7 to work on RN.

This is the work for building my app called [Inkdrop](https://www.inkdrop.app/) - a cross-platform Markdown note-taking app that uses PouchDB for accomplishing its data sync feature.

## How to use

Check out the fork repository:

{%github craftzdog/pouchdb-react-native %}

Also check out [a small example](https://github.com/craftzdog/pouchdb-react-native/tree/v7/example).

### Install

Install dev package:

```
yarn add -D babel-plugin-module-resolver
```

Install polyfill packages:

```sh
yarn add events process base-64 react-native-get-random-values react-native-quick-md5
```

Install pouchdb packages:

```sh
yarn add @craftzdog/pouchdb-core-react-native @craftzdog/pouchdb-binary-utils-react-native pouchdb-adapter-http pouchdb-mapreduce pouchdb-replication react-native-pouchdb-md5
```

Note: `@craftzdog/pouchdb-replication-react-native` is no longer needed.

Install storage adapter packages:

```sh
yarn add pouchdb-adapter-react-native-sqlite react-native-sqlite-2
```

Install CocoaPods:

```sh
cd ios && pod install
```

### Configure

Make a `shim.js`:

```js
import "react-native-get-random-values";
import { decode, encode } from "base-64";

if (typeof process === "undefined") {
 global.process = require("process");
} else {
 const bProcess = require("process");
 for (var p in bProcess) {
   if (!(p in process)) {
     process[p] = bProcess[p];
   }
 }
}

if (!global.btoa) {
 global.btoa = encode;
}

if (!global.atob) {
 global.atob = decode;
}

process.browser = true;
```

then, require it at the beginning of your `index.js`.

Edit your `babel.config.js` like so:

```js
module.exports = {
 presets: ['module:metro-react-native-babel-preset'],
 plugins: [
   [
     'module-resolver',
     {
       alias: {
         'pouchdb-md5': 'react-native-pouchdb-md5',
         'pouchdb-binary-utils':
           '@craftzdog/pouchdb-binary-utils-react-native',
       },
     },
   ],
 ],
};
```


## Hacking PouchDB (Again)

You can check out [what changes have been added](https://github.com/pouchdb/pouchdb/compare/master...craftzdog:v7) to the fork.

React Native has been evolving these 2 years, but `FileReader.readAsArrayBuffer` is still not implemented.
Creating blobs from `ArrayBuffer` or `ArrayBufferView` is not supported as well. I tried to make RN support them by looking into their source code but found that it's hard by design.
So, to deal with attachments in your databases, you have to avoid calling these APIs in PouchDB.

So, you have to hack `pouchdb-binary-utils` as following.
`blobOrBufferToBinaryString` calls `FileReader.readAsArrayBuffer`. Call `FileReader.readAsDataURL` instead:

```diff
--- a/packages/node_modules/pouchdb-binary-utils/src/blobOrBufferToBase64-browser.js
+++ b/packages/node_modules/pouchdb-binary-utils/src/blobOrBufferToBase64-browser.js
@@ -1,10 +1,16 @@
-import { btoa } from './base64';
-import blobOrBufferToBinaryString from './blobOrBufferToBinaryString';
-
 function blobToBase64(blobOrBuffer, callback) {
-  blobOrBufferToBinaryString(blobOrBuffer, function (base64) {
-    callback(btoa(base64));
-  });
+  if (blobOrBuffer.size) {
+    var reader = new FileReader();
+    reader.onloadend = function (e) {
+      const text = e.target.result || '';
+      const base64 = text.split(',')[1];
+      callback(base64);
+    };
+    reader.readAsDataURL(blobOrBuffer);
+  } else {
+    const base64 = blobOrBuffer.toString('base64');
+    setImmediate(() => callback(base64));
+  }
 }
```

In `readAsBinaryString`, use `blobToBase64` instead of `readAsArrayBuffer`:

```diff
--- a/packages/node_modules/pouchdb-binary-utils/src/readAsBinaryString.js
+++ b/packages/node_modules/pouchdb-binary-utils/src/readAsBinaryString.js
@@ -1,32 +1,10 @@
-//Can't find original post, but this is close
-//http://stackoverflow.com/questions/6965107/ (continues on next line)
-//converting-between-strings-and-arraybuffers
-function arrayBufferToBinaryString(buffer) {
-  var binary = '';
-  var bytes = new Uint8Array(buffer);
-  var length = bytes.byteLength;
-  for (var i = 0; i < length; i++) {
-    binary += String.fromCharCode(bytes[i]);
-  }
-  return binary;
-}
+import blobToBase64 from './blobOrBufferToBase64';

-// shim for browsers that don't support it
 function readAsBinaryString(blob, callback) {
-  var reader = new FileReader();
-  var hasBinaryString = typeof reader.readAsBinaryString === 'function';
-  reader.onloadend = function (e) {
-    var result = e.target.result || '';
-    if (hasBinaryString) {
-      return callback(result);
-    }
-    callback(arrayBufferToBinaryString(result));
-  };
-  if (hasBinaryString) {
-    reader.readAsBinaryString(blob);
-  } else {
-    reader.readAsArrayBuffer(blob);
-  }
+  blobToBase64(blob, (base64) => {
+    const binaryString = atob(base64);
+    callback(binaryString);
+  });
 }

 export default readAsBinaryString;
```

PouchDB tries to use `Blob` but it does not work as expected due to lack of `Blob` support in RN. So, we have to deal with attachments always in base64:

```diff
--- a/packages/node_modules/pouchdb-core/src/adapter.js
+++ b/packages/node_modules/pouchdb-core/src/adapter.js
@@ -720,7 +720,7 @@ AbstractPouchDB.prototype.getAttachment =
     }
     if (res.doc._attachments && res.doc._attachments[attachmentId]) {
       opts.ctx = res.ctx;
-      opts.binary = true;
+      opts.binary = false;
       self._getAttachment(docId, attachmentId,
                           res.doc._attachments[attachmentId], opts, callback);
     } else {
```

`pouchdb-md5` also calls `readAsArrayBuffer`.
I created [react-native-pouchdb-md5](https://github.com/craftzdog/react-native-pouchdb-md5) which natively calculates MD5 hashes.

Hope you find it useful and helpful.

* * *

- Follow me on [Twitter](https://twitter.com/inkdrop_app) & [Instagram](https://instagram.com/craftzdog)
- [Inkdrop — Markdown note-taking app](https://www.inkdrop.app/)
- [Solo developer's Discord community](https://discord.gg/S7hDmvh)

[![Subscribe Newsletter](https://miro.medium.com/proxy/1*-J5tp1_lulY9QknoLEv6zA.png)](http://eepurl.com/dNgJo6)

[![My YouTube channel](https://miro.medium.com/proxy/1*r-daItizHpAU-fQZk3ISag.png)](https://www.youtube.com/c/devaslife)