npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

cordova-plugin-document

v1.0.0

Published

UIDocument wrapper

Readme

cordova-plugin-Document

  • Create, read, and write text and binary documents--locally and outside the app's sandbox--including iCloud documents as well as documents shared by other apps.

  • Resolve iCloud and shared document conflicts caused by other apps or devices updating shared documents.

Document URLs can be specified programmatically--in system directories or ubiquity containers--or documents can be selected by a Document Picker. Documents can be created in a Document-Picker-selected directory.

Creating/reading/writing local unshared documents is relatively simple, but handling Cloud documents and documents shared between apps can be much more complex because there can be conflicts which must be resolved. Also, suspended app's shared documents can get out of sync.

If a shared document is treated as read-only, the only additional consideration is handling the "document-updated" notification.

If all you want to do is read a file selected by a Document Picker, other plugins do this more easily in a cross-platform way.

Minimum supported Deployment Target is iOS14. iOS16 is required to support all features.

What’s Ahead

  • Bookmarks
  • Additional creation options

Installation

cordova plugin add cordova-plugin-document

Getting Started

The best way is to build the Demo App. This app demonstrates the following:

  • Programmatic creation of text and binary documents in the Documents Directory.
  • Programmatic creation of an iCloud text document.
  • Document creation in a directory user-selected via the Document Picker.
  • Opening Documents programmatically and via the Document Picker.
  • Editing text and binary documents.
  • Resolving iCloud Document Conflicts.
  • https://github.com/j-crosson/cordova-document-demo

Example

//creates 'untitled.txt' in 'Documents' directory
iDocument.documentAction ('create', docCreated, operationFailed,['untitled.txt'])}; 

 // save text
function docCreated() {
    let text = "this is text";
    iDocument.documentAction("save",docSaved,saveFailed,[text]);
}

// close document
function docSaved() {
    iDocument.documentAction ("close",operationComplete, closeFailed);
}

documentAction

iDocument.documentAction(action,success,error,arguments[])

Perform Document Action such as creating, reading, and writing documents.

| Parameter | Type | Description | Default | | --- | --- | --- |--- | | action | String | action to perform | | | success | Function | Success callback function| null | | error | Function | Error callback | null| | arguments | [String] | action data | [""]|

actions:


create


Create Document

Creates a document. Either supply a directory and filename or let user choose a directory. Directory choices that are not user selected are limited to system directories. Creates 'untitled.txt' in the "Documents" directory if no arguments supplied.

| arguments | Type | Description | Default | | --- | --- | --- |--- | | filename | String | file name and extension |untitled.txt | | createOptions | createOptions | action to perform | overwrite + utf8 | | documentDirectory | String | file's system directory | "documentsDirectory"|

filename

Name and extension of file to be created. The extension can be whatever you like but the plugin uses Uniform Type Identifiers (UTIs) or extensions that Apple recognizes.

createOptions

File creation options.

iDocument.createOptions:

| option | Description | | --- | --- | | overwrite | overwrite file if it exists | | utf8 | utf8 text file | | bin | binary file | | getDir | user supplies directory |

const options  = iDocument.createOptions.utf8 + iDocument.createOptions.overwrite;
// this is the default 

For now, overwrite is the only write option, utf8 is the only text option. These are the defaults.

documentDirectory

System directory in which file is created.

Supported directories:

  • "documentsDirectory"
  • "temporaryDirectory"
  • "cachesDirectory"
  • "libraryDirectory"

Apple documentation explains the use and behavior of these directories. If it is desired to use other system directories, "systemDirs" in DocumentView.swift has a selection of system directories most of which are commented out and can be reactivated. Some are Mac-only, some work, some don't.

If an app's Deployment Target is less tham iOS 16, "documentsDirectory" is the only option.

Cloud Directory

  • "iCloud"

The only way this plugin can programmatically create a file on iCloud is to specify "iCloud" for "documentDirectory". This will create the file "filename" in the "Documents" directory of the default Ubiquity Container.

success

Document Creation succeeded callback.

Returns "Succeeded" status which can be safely ignored since it contains no additional info.

error

Document Creation failed callback.

Returns:

  • returnStatus.savingError -- document save failed
  • returnStatus.badPath -- path was bad
  • returnStatus.duplicate -- there is another document open
  • returnStatus.badArguments -- couldn't parse arguments
  • returnStatus.badFilename -- bad file name
  • returnStatus.badOptions -- options argument was bad
  • returnStatus.badDirectoryArg -- bad directory
  • returnStatus.unexpectedError -- unknown error
  • returnStatus.userCancelled -- user cancelled directory picker
//creates 'untitled.txt' in 'Documents' directory
iDocument.documentAction ('create', docCreated, operationFailed)};  

//creates 'untitled.txt' in 'Documents' directory
iDocument.documentAction ('create', docCreated, operationFailed,['untitled.txt'])}; 
  
//creates 'untitled.txt' in 'Documents' directory
iDocument.documentAction ('create', docCreated, operationFailed,['untitled.txt',iDocument.createOptions.overwrite,'documentsDirectory'])};
   
//creates 'untitled.txt' in user-selected directory
iDocument.documentAction ('create', docCreated, operationFailed,['untitled.txt',iDocument.createOptions.getDir])};

function operationFailed(status) {
    if(status.includes(iDocument.returnStatus.userCancelled)) {
    // user changed mind
    }
}


openDocument


Open document for read/write.

| arguments | Type | Description | Default | | --- | --- | --- |--- | | filename | String | file name and extension |untitled.txt | | documentDirectory | String | file's system directory | "documentsDirectory"| | isBinary | Bool | is file binary | False|

filename

Name and extension of file to be opened.

documentDirectory

System directory in which file exists.

Supported directories:

  • "documentsDirectory"
  • "temporaryDirectory"
  • "cachesDirectory"
  • "libraryDirectory"
  • "iCloud"

See "create" for additional "documentDirectory" info.

isBinary

If "True" file will be treated as binary.

success

Document Open succeeded callback.

Returns text or binary document data.

// text data
function textDocOpened(text) {
   console.log(text);
}

// binary data
 function binDocOpened(bin) {
   let bytes = new Uint8Array(bin);
 }

error

Returns:

  • returnStatus.doesntExist -- file doesn't exist

  • returnStatus.badArguments -- couldn't parse arguments

  • returnStatus.badFilename -- bad file name

  • returnStatus.badDirectoryArg -- bad directory

  • returnStatus.badPath -- path was bad

  • returnStatus.badBinArg -- isBinary argument is bad

// opens 'untitled.txt' in 'Documents' directory
iDocument.documentAction("openDocument",docOpened,openFailed,["untitled.txt","documentsDirectory"]);

// opens 'untitled.bin' as binary file in 'Documents' directory
iDocument.documentAction("openDocument",binDocOpened,openFailed,["untitled.bin","documentsDirectory",true]);

function openFailed(status) {
    if(status.includes(iDocument.returnStatus.doesntExist)) {
    // file not found
    }
}


selectDocument


Opens user-selected document for read/write.

| arguments | Type | Description | Default | | --- | --- | --- |--- | | documentDirectory | String | initial directory | | | UTIs | [String] | document types to open by Uniform Type Identifier | [public.plain-text]| | extensions | [String] | document types to open by extension | [txt]| | isBinary | Bool | is file binary | False|

documentDirectory

The directory initially displayed by the document picker. If null or an empty string then the picker will show what iOS considers to be the current directory.

Supported directories:

  • "documentsDirectory"
  • "temporaryDirectory"
  • "cachesDirectory"
  • "libraryDirectory"

See "create" for additional "documentDirectory" info.

isBinary

If "True" file will be treated as binary.

UTIs

An array of UTIs that the document picker can open (public.plain-text, public.rtf, etc.) The default is "public.plain-text"

extensions

An array of extensions that the document picker can open (txt, jpg, etc.) The default is "txt". The extensions must be recognized by iOS.

UTIs and extensions can be used simultaneously but it is a good idea to use one or the other.

success

Document Select succeeded callback.

Returns text or binary document data.

// text data
function textDocOpened(text) {
   console.log(text);
}

// binary data
 function binDocOpened(bin) {
   let bytes = new Uint8Array(bin);
 }

error

Document Select failed callback.

Returns:

  • returnStatus.userCancelled -- user cancelled directory picker
  • returnStatus.badExtensionsArg -- extensions argument is bad
  • returnStatus.badUTIArg -- UTIs argument is bad
  • returnStatus.badArguments -- couldn't parse arguments
  • returnStatus.badDirectoryArg -- bad directory
  • returnStatus.badPath -- path was bad
  • returnStatus.badBinArg -- isBinary argument is bad
  • returnStatus.unexpectedError -- unexpected error

//both lines select text documents

iDocument.documentAction("selectDocument",textDocOpened,selectFailed,["documentsDirectory",[],["txt"]]);

iDocument.documentAction("selectDocument",textDocOpened,selectFailed,["documentsDirectory",["public.plain-text"],[]]);

function selectFailed(status) {
    if(status.includes(iDocument.returnStatus.userCancelled)) {
    // user cancelled picker
    }
}


save


Save document.

Will only save main document. Documents opened for conflict resolution are read only.

| arguments | Type | Description | | --- | --- | --- | | data | Data | saved data |

data

Saved data

success

Document Save succeeded callback.

Returns status:

  • returnStatus.normal -- normal state, always present
  • returnStatus.currentDocument -- always present, can only save current document

error

Document Save failed callback.

Returns:

  • returnStatus.doesntExist -- document doesn't exist
  • returnStatus.savingError -- iOS returned saving error
  • returnStatus.closed -- document closed
  • returnStatus.inConflict -- conflict exists
  • returnStatus.badArguments -- couldn't parse arguments
  • returnStatus.normal -- normal status but there was an additional disqualifing status
  • returnStatus.progressAvailable -- update in progress
  • returnStatus.editingDisabled -- editing disabled, update in progress

For a local document the "closed" status means the document is closed, however, for a cloud document the status could be a link in the document update chain, a temporary status that will eventually resolve to "normal". In the cloud "closed" case (and in the case of other non-"normal" statuses) the document is being updated externally and the "save" can be aborted and handled when the "update" (iDocument.loaded) notification is recieved.
The plugin will not allow a cloud document that is in conflict to be saved. If the only status that is preventing a save is "inConflict" (not closed but "inConflict" and "normal"), the conflict must be resolved before a save is allowed. An additional status such as "editingDisabled" indicates that an update is in progress and an "update" notification will be recieved upon completion of the update.

//text
   let text = "this is text";
   iDocument.documentAction("save",docSaved,saveFailed,[text]);

// binary data "00,01,02,FF"
   let arr8 = new Uint8Array([0, 1, 2, 255]);
   iDocument.documentAction("save",docSaved,  saveFailed, [arr8.buffer]);

close


Close document.

Typically there will not be a document ID supplied unless working with conflict documents.

| arguments | Type | Description | Default | | --- | --- | --- |--- | | ID | documentID | document to close | documentID.primary|

documentID:

| ID | Description | | --- | --- | | primary | primary document | | otherDocument | iCloud conflict document |

success

Close document succeeded callback.

Returns:

  • returnStatus.closed -- document is closed

  • returnStatus.currentDocument || returnStatus.conflictDocument-- ID of closed document

  • returnStatus.inConflict -- if closed document was in conflict

error

Close document failed callback.

Returns:

  • returnStatus.doesntExist -- document doesn't exist
  • returnStatus.badArguments -- couldn't parse arguments
  //typical document close
    iDocument.documentAction ("close",docClosed, operationFailed);

 //close conflict resolution document
    iDocument.documentAction ("close",docClosed, operationFailed,[iDocument.documentID.otherDocument]);

getStatus


Returns document status.

Typically there will not be a document ID supplied.

| arguments | Type | Description | Default | | --- | --- | --- |--- | | ID | documentID | status document | documentID.primary|

iDocument.documentID:

| ID | Description | | --- | --- | | primary | primary document | | otherDocument | iCloud conflict document |

success

Returns status.

Returns:

  • returnStatus.doesntExist -- document doesn't exist
  • returnStatus.normal -- normal state
  • returnStatus.editingDisabled -- editing disabled

This status is returned when a document is being externally updated. When the update is complete, an update notification (iDocument.loaded) will be sent at which time suspended operations can resume.

  • returnStatus.currentDocument -- document is current document
  • returnStatus.conflictDocument -- document is conflict document
  • returnStatus.closed -- document closed

For a local document the "closed" status means the document is closed, however, for a cloud document the status could be a link in the document update chain, a temporary status that will eventually resolve to "normal". In the cloud "closed" case (and in the case of other non-"normal" statuses) if the document is being updated externally an "update" (iDocument.loaded) notification will be sent when the update is complete.

  • returnStatus.inConflict -- conflict exists

Current and conflict version(s) of the document exist. Needs to be resolved before the document can be saved.

  • returnStatus.progressAvailable

    The plugin doesn't use progress information but this status indicates that the document is being updated. When the update is complete, an update notification (iDocument.loaded) will be sent.

Testing for absence of "normal" (which is what the "save" option does) is usually sufficient.

error

get status failed callback.

Returns:

  • returnStatus.doesntExist -- document doesn't exist
  • returnStatus.badArguments -- couldn't parse arguments
    //get status of document
    iDocument.documentAction ("getStatus",docStatus, operationFailed);

    //get status of conflict resolution document
    iDocument.documentAction ("getStatus",docStatus, operationFailed,[iDocument.documentID.otherDocument]);
    
    function docStatus (status) {
        if(status.includes(iDocument.returnStatus.inConflict)) {
        // do something
        }    
    }


getData


Retrieve document data from plugin.

When a document is opened, document data is returned: it is not necessary to do a "getData" unless you want a fresh copy, maybe to undo changes. If a document is externally updated, you are notified and can retrieve the new data with "getData".

| arguments | Type | Description | | --- | --- | --- | | ID | documentID | status document |

iDocument.documentID:

| ID | Description | | --- | --- | | primary | primary document | | otherDocument | iCloud conflict document |

success

Returns Document data.

error

get data failed callback.

Returns:

  • returnStatus.doesntExist -- document doesn't exist
  • returnStatus.badArguments -- couldn't parse arguments
    iDocument.documentAction("getData",updateRetrieved,operationFailed,[iDocument.documentID.primary]);
    
    function updateRetrieved (theData) {
        console.log(theData);
    }

resolve


Resolves document conflicts.

Documents that can be externally updated (iCloud documents, for example) can have conflicting versions. When there is a conflict, there is a current version of the document and one or more conflict versions. To resolve the conflict, a "conflict winner" version needs to be selected. This version will supersede all other versions. The current version or one of the conflict versions can be chosen as the winner or the versions can be merged. To merge versions, select the current version as the conflict winner and then save the merged version.

"Current version" is what iCloud thinks is the current version, not necessarily what is currently displaying. The plugin will have the current version and will have sent an "update" notification--perhaps this has triggered the resolution process--but the plugin data and local data will be out of sync until a "getData" happens.

Before "resolve" can be called, "getOtherVersions" must be called first.

| arguments | Type | Description | | --- | --- | --- | | resolutionType | String | file's system directory | | versionNumber | Int| selected version |

resolutionType

Type of conflict resolution.

Supported resolutions:

  • "current"

Selects current version as document version.

  • "version"

Selects conflict version "versionNumber" as document version.

versionNumber

Conflict version to assign to document.

// resolve to current version
  iDocument.documentAction("resolve",finished,operationFailed,["current"]);

// resolve to conflict document "0"      
  iDocument.documentAction("resolve",finished,operationFailed,["version",0]);    

getOtherVersions


Get other (conflict) versions of the document.

success

Returns number of other versions. After "getOtherVersions" is called, "openOther" can open other versions by version number. Other documents are available until "resolve" resolves conflicts.

error

Failed.

Returns:

  • returnStatus.doesntExist -- document doesn't exist
  • returnStatus.noVersions -- there are not any other versions
  • returnStatus.unexpectedError -- unexpected error
iDocument.documentAction("getOtherVersions",resolve,failed);
        
function resolve (numberOfVersions) {
     //if "getOtherVersions" succeeds, there is at least one other version so "0" will work
     iDocument.documentAction("openOther",getOther,operationFailed,[0]);
}

openOther


Opens other document "documentNumber." These documents are "conflict" documents

The primary document should remain open while other versions of the document are opened and closed. Only one "other" document can be open a time. "Other" documents should be closed before "resolve" is attempted.

| arguments | Type | Description | | --- | --- | --- | | documentNumber | Int| document version to open |

documentNumber

"getOtherVersions" returns the number of existing Other (conflict) Versions. "documentNumber" is a zero-based index into these Other Versions.

success

Document Open succeeded callback.

Returns other document data.

error

Returns:

  • returnStatus.doesntExist -- file doesn't exist

  • returnStatus.badArguments -- couldn't parse arguments

  • returnStatus.noVersions -- there are not any other versions (or "getOtherVersions" was not previously called)

 
iDocument.documentAction("openOther",getOther,operationFailed,[0]);
      
      
function getOther(data) {
console.log(data);
iDocument.documentAction ("close",resolveWithOther, operationFailed,[iDocument.documentID.otherDocument]);
}

Notifications

There is a single notification that needs to be handled: "loaded," which is triggered when new document data is loaded. This notification is only useful for iCloud or shared documents. Apps that only deal with local documents do not need to handle this notification. The app will recieve this notification when another device or app updates a document. The new document data can be retrieved via a "getData." The app will also be notified if a new document is downloaded as the result of a conflict resolution.

iDocument.loaded = [the Event Handler];
function EventHandler(documentStatus)

| Param | Type | Description | | --- | --- | --- | | documentStatus | returnStatus| Current Document Status |

See "getstatus" for description of "returnStatus"

This notification returns two additional status entries:

  • returnStatus.typeUTF8 -- file type is UTF8
  • returnStatus.typeBin -- file type is binary
function onDeviceReady() {
        iDocument.loaded = newData;
}


function newData(documentStatus) { 
    if(documentStatus.includes(iDocument.returnStatus.inConflict) {
    //do something
    } else {
    //do something else
    }
}

Configuration

No configuration is necessary to deal with unshared local documents. To share documents or access iCloud, there are multiple properties that need to be set. The following list is far from inclusive. There are many options depending on your app goals. These options are not listed here, this is only a brief list to get started.

iCloud

A paid developer account is necessary to test the plugin with iCloud.

In the Project Configuration Signing & Capabilities pane, for "iCloud" select "iCloud Documents" and optionally select a container. There are configuration keys for other options, making the container public (NSUbiquitousContainerIsDocumentScopePublic), for example.

Property List Keys

UISupportsDocumentBrowser

Privacy Manifest file (PrivacyInfo.xcprivacy)

Your app will require a Privacy Manifest. Here is a manifest fragment that applies to this plugin when using default functionality:


<dict>
  <key>NSPrivacyTracking</key>
  <false/>
  <key>NSPrivacyTrackingDomains</key>
  <array/>
  <key>NSPrivacyAccessedAPITypes</key>
  <array>
      <dict>
          <key>NSPrivacyAccessedAPIType</key>
          <string>NSPrivacyAccessedAPICategoryDiskSpace</string>
          <key>NSPrivacyAccessedAPITypeReasons</key>
          <array>
              <string>E174.1</string>
          </array>
      </dict>
      <dict>
          <key>NSPrivacyAccessedAPIType</key>
          <string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
          <key>NSPrivacyAccessedAPITypeReasons</key>
          <array>
              <string>C617.1</string>
          </array>
      </dict>
  </array>
  <key>NSPrivacyCollectedDataTypes</key>
  <array/>
</dict>

Conflict resolution.

iCloud documents can be modified on multiple devices leading to conflicts. There are a couple of cases that have to be handled. The first case occurs when another device updates a document you are working on. This technically isn't a conflict and isn't marked as such. You will receive a notification that a new version of the document is available and you will need to do a "getData" to retrieve it. At this point you can merge the new version with the local version or otherwise resolve the conflict. The second case occurs when iCloud detects a conflict. One way this can be caused is if an offline device changes a document which your online device has also modified. When the offline device comes online and syncs with iCloud, a conflict version of the document is created. The conflict version(s) (there can be more than one) can be opened and you can merge the version(s) with the local version or otherwise resolve the conflict. You could, for example, make one of the conflict versions the current version. In general, a conflict can be detected via a notification or by the status returned when the document is opened (or by any other action that returns a status, including a status request).

Documents shared between apps can also have conflicts and can be resolved in a similar way.

Suspended apps don’t get notifications: the app doesn’t know if a document has been updated. It’s necessary to close and reopen the document to see if there were any changes while suspended.

Suspended Apps

If an app is suspended it doesen't recieve notification that a document has been updated thus potentially rendering a local shared document out of sync with the current document. It is necessary to refresh the document when the app becomes active.

Background Info

A little bit about what's going on "behind the scenes"

The plug-in uses a UIDocument to handle the document I/O functions. UIDocument simplifies handling iCloud documents and conflicts as well as providing other features, most of which we don't use.

The UIDocument has its own copy of document data. This data is retrieved on an "open" or updated on a "save". The UIDocument can be externally updated--rendering the viewed document out of sync--at which point the plugin sends a "document-updated" notification. A "getData" is used to retrieve the updated document data.

A typical UIDocument is autosaved periodically and relies on notifications to track state changes. Autosave doesn't make sense in our case since the UIDocument doesn't change continuously: we have to send our document data across the JavaScript/native bridge to the UIDocument (our "save" operation) at which point we initiate a save which will call our success/fail callback on completion. The plug-in doesn't expose the state notifications: they aren't useful given our approach. The plug-in can request document state and operations such as “open” and “save” return document state.

UIDocument as well as this plugin is not a good choice for large documents.