craftzdog / pouchdb-react-native-demo

A working demo for PouchDB on React Native with SQLite3 storage

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

A Demo for Running PouchDB on React Native with SQlite3

Run Instructions

Clone the repo and run the following commands in the project root directory:

npm i
npm start

Then open a separate terminal window and run the following in the project root directory:

react-native run-ios

or

react-native run-android

This will open a device emulator to the demo app.

Click "Run Demo" at the top of the device emulator screen. This will create a PouchDB instance and write and read a basic document with an attachment.

CouchDB Synchronization

If you want to run with CouchDB synchronization, uncomment the relevant code in the populateDatabase in App.js and replace YOUR_SERVER with your server URL or IP address.

Background

Please read this blogpost.

How I hacked PouchDB

To get it to work on React Native with attachments support, we need to avoid calling FileReader.readAsArrayBuffer from PouchDB core modules since RN does not support it yet. That means we always process attachments in Base64 instead of Blob. It can be done with a few lines of code hacks.

Where readAsArrayBuffer is called

PouchDB tries to calclulate MD5 digest for every document, which needs to call readAsArrayBuffer.

In pouchdb-binary-utils/lib/index-browser.js:

72 function readAsBinaryString(blob, callback) {
73   if (typeof FileReader === 'undefined') {
74     // fix for Firefox in a web worker
75     // https://bugzilla.mozilla.org/show_bug.cgi?id=901097
76     return callback(arrayBufferToBinaryString(
77       new FileReaderSync().readAsArrayBuffer(blob)));
78   }
79
80   var reader = new FileReader();
81   var hasBinaryString = typeof reader.readAsBinaryString === 'function';
82   reader.onloadend = function (e) {
83     var result = e.target.result || '';
84     if (hasBinaryString) {
85       return callback(result);
86     }
87     callback(arrayBufferToBinaryString(result));
88   };
89   if (hasBinaryString) {
90     reader.readAsBinaryString(blob);
91   } else {
92     reader.readAsArrayBuffer(blob);
93   }
94 }

This function is called from pouchdb-md5/lib/index-browser.js:

24 function appendBlob(buffer, blob, start, end, callback) {
25   if (start > 0 || end < blob.size) {
26     // only slice blob if we really need to
27     blob = sliceBlob(blob, start, end);
28   }
29   pouchdbBinaryUtils.readAsArrayBuffer(blob, function (arrayBuffer) {
30     buffer.append(arrayBuffer);
31     callback();
32   });
33 }

Well, how to avoid that?

Storing Attachments

Disable binary option of getAttachment method in pouchdb-core/src/adapter.js like so:

714     if (res.doc._attachments && res.doc._attachments[attachmentId]
715       opts.ctx = res.ctx;
716       // force it to read attachments in base64
717       opts.binary = false;
718       self._getAttachment(docId, attachmentId,
719                           res.doc._attachments[attachmentId], opts, callback);
720     } else {

With this change, you will always get attachments encoded in base64.

Pull Replication

We have to convert blob to base64 when fetching attachments from remote database in pouchdb-replication/lib/index.js like so:

function getDocAttachmentsFromTargetOrSource(target, src, doc) {
  var doCheckForLocalAttachments = pouchdbUtils.isRemote(src) && !pouchdbUtils.isRemote(target);
  var filenames = Object.keys(doc._attachments);

  function convertBlobToBase64(attachments) {
    return Promise.all(attachments.map(function (blob) {
      if (typeof blob === 'string') {
        return blob
      } else {
        return new Promise(function (resolve, reject) {
          var reader = new FileReader();
          reader.readAsDataURL(blob);
          reader.onloadend = function() {
            const uri = reader.result;
            const pos = uri.indexOf(',')
            const base64 = uri.substr(pos + 1)
            resolve(base64)
          }
        });
      }
    }));
  }

  if (!doCheckForLocalAttachments) {
    return getDocAttachments(src, doc)
      .then(convertBlobToBase64);
  }

  return target.get(doc._id).then(function (localDoc) {
    return Promise.all(filenames.map(function (filename) {
      if (fileHasChanged(localDoc, doc, filename)) {
        return src.getAttachment(doc._id, filename);
      }

      return target.getAttachment(localDoc._id, filename);
    }))
      .then(convertBlobToBase64);
  }).catch(function (error) {
    /* istanbul ignore if */
    if (error.status !== 404) {
      throw error;
    }

    return getDocAttachments(src, doc)
      .then(convertBlobToBase64);
  });
}

That worked! And that's why I made both @craftzdog/pouchdb-core-react-native and @craftzdog/pouchdb-replication-react-native. If you found any problem on them, pull requests would be welcomed here.

About

A working demo for PouchDB on React Native with SQLite3 storage

License:MIT License


Languages

Language:JavaScript 44.9%Language:Objective-C 29.7%Language:Starlark 14.8%Language:Java 10.7%