About 2 years ago, I hacked PouchDB@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 - a cross-platform Markdown note-taking app that uses PouchDB for accomplishing its data sync feature.
How to use
Check out the fork repository:
craftzdog / pouchdb-react-native
๐จ - PouchDB is a pocket-sized database, with some patches for running on React Native
PouchDB for React Native
A performant fork of PouchDB for React Native.
NOTE: The attachment support has been dropped since I no longer store binary data in PouchDB.
How to use PouchDB on React Native with SQLite
Install
-
Install dev package:
yarn add -D babel-plugin-module-resolver
-
Install polyfill packages:
yarn add events react-native-get-random-values react-native-quick-base64
-
Install pouchdb packages:
yarn add pouchdb-core pouchdb-replication pouchdb-mapreduce pouchdb-adapter-http
-
Install patched package:
yarn add @craftzdog/pouchdb-collate-react-native
It avoids using '\u0000' chars for indexing (See this commit).
-
Install storage adapter packages:
yarn add pouchdb-adapter-react-native-sqlite react-native-quick-sqlite react-native-quick-websql
- react-native-quick-sqlite - A fast bindings via JSI for SQLite
- react-native-quick-websql - WebSQL wrapper for quick-sqlite
- pouchdb-adapter-react-native-sqlite - PouchDB adapter for SQLite with those two modules
-
Install CocoaPods:
npx pod-install
Configure
-
Make a
shim.js
:import {shim} from 'react-native-quick-base64' shim() // Avoid using node dependent modules process.browser = true
then, require it at the beginning ofโฆ
Also check out a small example.
Install
Install dev package:
yarn add -D babel-plugin-module-resolver
Install polyfill packages:
yarn add events process base-64 react-native-get-random-values react-native-quick-md5
Install pouchdb packages:
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:
yarn add pouchdb-adapter-react-native-sqlite react-native-sqlite-2
Install CocoaPods:
cd ios && pod install
Configure
Make a shim.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:
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 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:
--- 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
:
--- 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:
--- 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 which natively calculates MD5 hashes.
Hope you find it useful and helpful.
Top comments (2)
Hi Takuya, thank you very much for your contribution really, excellent work. I imagine that many are in the same situation. I am trying to implement this approach using Expo. Everything works ok but since i need to use sqlite adapter and i am using expo SQLite (docs.expo.io/versions/latest/sdk/s...) i get invalid adapter error. Do you know how I could solve this issue without having to do Expo eject to use react-native-sqlite-2 ?
you need websql-compatible database.