Skip to content

sandyclock/cordova-plugin-shared

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

90 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cordova-plugin-shared

This plugin for Apache Cordova registers your app to handle certain types of files.

Overview

This is a bit modified version of cordova-plugin-openwith by Jean-Christophe Hoelt for iOS.

What's different:

  • Works with several types of shared data (UTIs). Currently, URLs, text and images are supported. If you would like to remove any of these types, feel free to edit ShareExtension-Info.plist (NSExtensionActivationRule section) after plugin's installation
  • Support of sharing several photos at once is supported. By default, the maximum number is 10, but this can be easily edited in the plugin's .plist file
  • Ability to check if the user is logged in or not in your app. If not logged in, a native interface alert message will be displayed instead of starting your app.
  • Does not show native UI with "Post" option. Having two-step share (enter sharing message and then pick the receiver in the Cordova app) might be a bad user experience, so this plugin opens Cordova application immediately and passes the shared data to it. Thereby, you are expected to implement sharing UI in your Cordova app.

This plugin refers only to iOS, so the Android parts have been cut out both from the plugin and documentation.

You'd like your app to be listed in the Send to... section for certain types of files, on both Android and iOS? This is THE plugin! No need to meddle into Android's manifests and iOS's plist files, it's (almost) all managed for you by a no brainer one liner installation command.

Table of Contents

iOS

On iOS, there are many ways apps can communicate. This plugin uses a Share Extension. This is a particular type of App Extension which intent is, as Apple puts it: "to post to a sharing website or share content with others".

A share extension can be used to share any type of content. You have to define which you want to support using an Universal Type Identifier (or UTI). For a full list of what your options are, please check Apple's System-Declared UTI.

As with all extensions, the flow of events is expected to be handled by a small app, external to your Cordova App but bundled with it. When installing the plugin, we will add a new target called ShareExtension to your XCode project which implements this Extension App. The Extension and the Cordova App live in different processes and can only communicate with each other using inter-app communication methods.

When a user posts some content using the Share Extension, the content will be stored in a Shared User-Preferences Container. To enable this, the Cordova App and Share Extension should define a group and add both the app and extension to it.

Once the data is in place in the Shared User-Preferences Container, the Share Extension will open the Cordova App by calling a Custom URL Scheme. This seems a little borderline as Apple tries hard to prevent this from being possible, but brave iOS developers always find solutions... So as for now there is one and it seems like people got their app pass the review process with it. At the moment of writing, this method is still working on iOS 11.1. The recommended solution is be to implement the posting logic in the Share Extension, but this doesn't play well with Cordova Apps architecture...

On the Cordova App side, the plugin checks listens for app start or resume events. When this happens, it looks into the Shared User-Preferences Container for any content to share and report it to the javascript application.

Installation

Here's the promised one liner:

cordova plugin add cordova-plugin-shared \
  --variable IOS_URL_SCHEME=cordovaopenwithdemo
variable example notes
ANDROID_MIME_TYPE image/* Mime type of documents you want to share (wildcards accepted)
IOS_URL_SCHEME uniquelonglowercase Any random long string of lowercase alphabetical characters
DISPLAY_NAME My App Name If you want to use a different name than your project name
PROVISIONING_PROFILE a71204b0-8187-4dcb-a343-1d43bd543c76 The provisioning profile ID of your share extension App ID
DEVELOPMENT_TEAM ABCDEFGHIJ Your Apple team ID

It shouldn't be too hard. But just in case, Jean-Christophe Hoelt posted a screencast of it.

Usage with Capacitor

When using this plugin with Capacitor, Cordova hooks do not run automatically. This means that placeholder strings in the plugin's source files are not replaced when Capacitor syncs the plugin. You need to handle this manually.

Automatic Fix Script

The easiest solution is to use the provided fix script that automatically replaces placeholders after npx cap sync. The scripts are included in the plugin and can be referenced directly:

Add a Capacitor hook in package.json to automatically run the fix after npx cap sync:

"scripts": {
  "capacitor:sync:after": "./node_modules/cordova-plugin-shared/scripts/fix-cordova-plugin-shared.sh"
}

Note:

  • Capacitor automatically runs scripts with the capacitor:sync:after name after npx cap sync completes. This is similar to Cordova hooks but uses npm script naming conventions.
  • The script is referenced directly from node_modules, so no copying is needed. If the plugin is removed, npx cap sync will naturally error out when it tries to run the missing script.

The script will automatically:

  • On fresh install (if ios/App/ShareExt doesn't exist or is empty):

    • Copy Share Extension files from the plugin source to ios/App/ShareExt/
    • Replace all placeholders in the copied files
  • On existing setup (if ios/App/ShareExt already exists):

    • Check existing files for placeholders and fix them if needed
    • Never removes or overwrites existing files - only fixes placeholders
  • Always checks:

    • The Capacitor copied file at ios/capacitor-cordova-ios-plugins/sources/CordovaPluginShared/ShareViewController.h
    • Extracts bundle ID from Xcode project or existing ShareViewController.h
    • Extracts URL scheme from ShareViewController.h or capacitor.config.ts
    • Replaces __BUNDLE_IDENTIFIER__, __URL_SCHEME__, __DISPLAY_NAME__, etc. with actual values

Manual Fix (Alternative)

If you prefer to fix it manually or the script doesn't work:

  1. After running npx cap sync, check if the file ios/capacitor-cordova-ios-plugins/sources/CordovaPluginShared/ShareViewController.h contains placeholders:

    grep "__BUNDLE_IDENTIFIER__\|__URL_SCHEME__" ios/capacitor-cordova-ios-plugins/sources/CordovaPluginShared/ShareViewController.h
  2. If placeholders are found, replace them:

    • Replace __BUNDLE_IDENTIFIER__ with your share extension's bundle ID (e.g., com.example.myapp.shareextension)
    • Replace __URL_SCHEME__ with your URL scheme (e.g., myapp)

    The file should look like:

    #define SHAREEXT_GROUP_IDENTIFIER @"group.com.example.myapp.shareextension"
    #define SHAREEXT_URL_SCHEME @"myapp"

Optional: Copy Scripts to Project

If you prefer to have the scripts in your project's scripts/ directory (for version control or customization), you can copy them:

cp node_modules/cordova-plugin-shared/scripts/fix-cordova-plugin-shared.sh scripts/
chmod +x scripts/fix-cordova-plugin-shared.sh

Then update package.json to reference the local copy:

"capacitor:sync:after": "./scripts/fix-cordova-plugin-shared.sh"

Important Notes

  • This file is regenerated every time you run npx cap sync, so you must either:

    • Use the automatic fix script (recommended)
    • Manually fix it after each sync
    • Add a post-sync hook to your build process
  • The script requires that you have:

    • A properly configured ShareExt target in Xcode, OR
    • A manually configured ios/App/ShareExt/ShareViewController.h file with the correct values
  • Bundle ID format: The script expects the bundle ID to include .shareextension (e.g., com.example.myapp.shareextension), and the App Group will be group.{BUNDLE_ID} (e.g., group.com.example.myapp.shareextension).

Troubleshooting

If the automatic fix script fails:

  1. Check that your ShareExt target is configured in Xcode with the correct bundle ID
  2. Verify your URL scheme is set in ios/App/ShareExt/ShareViewController.h
  3. Run the script manually to see error messages:
    ./scripts/fix-cordova-plugin-shared.sh

Available Scripts

The plugin includes two helper scripts in node_modules/cordova-plugin-shared/scripts/:

  • fix-cordova-plugin-shared.sh: Automatically fixes placeholder strings after npx cap sync
  • check-cordova-plugin-shared.sh: Checks for placeholders without modifying files (useful for CI/CD)

Usage:

  • Reference directly from node_modules in your package.json (recommended - no copying needed)
  • Or copy to your project's scripts/ directory if you want to customize them

Note: Both scripts automatically check if cordova-plugin-shared is installed. If the plugin is not found, they will:

  • Display an error message with clear instructions
  • Exit with code 1 (causing the hook to fail)
  • Provide two options: install the plugin or remove the script from package.json

If you reference the script directly from node_modules and the plugin is removed, npx cap sync will naturally error out when it tries to run the missing script, providing immediate feedback.

Usage

document.addEventListener('deviceready', setupOpenwith, false);

function setupOpenwith() {

  // Increase verbosity if you need more logs
  //cordova.openwith.setVerbosity(cordova.openwith.DEBUG);

  // Initialize the plugin
  cordova.openwith.init(initSuccess, initError);

  function initSuccess()  { console.log('init success!'); }
  function initError(err) { console.log('init failed: ' + err); }

  // Define your file handler
  cordova.openwith.addHandler(myHandler);

  function myHandler(intent) {
    console.log('intent received');
    console.log('  text: ' + intent.text); // description to the sharing, for instance title of the page when shared URL from Safari
    for (var i = 0; i < intent.items.length; ++i) {
      var item = intent.items[i];
      console.log('  type: ', item.uti);    // UTI. possible values: public.url, public.text or public.image
      console.log('  type: ', item.type);   // Mime type. For example: "image/jpeg"
      console.log('  data: ', item.data);   // shared data. For URLs and text - actually the shared URL or text. For image - its base64 string representation.
      console.log('  text: ', item.text);   // text to share alongside the item. as we don't allow user to enter text in native UI, in most cases this will be empty. However for sharing pages from Safari this might contain the title of the shared page.
      console.log('  name: ', item.name);   // suggested name of the image. For instance: "IMG_0404.JPG"
      console.log('  utis: ', item.utis);   // some optional additional info
    }
    // ...
    // Here, you probably want to do something useful with the data
    // ...
  }
}

API

cordova.openwith.setVerbosity(level)

Change the verbosity level of the plugin.

level can be set to:

  • cordova.openwith.DEBUG for maximal verbosity, log everything.
  • cordova.openwith.INFO for the default verbosity, log interesting stuff only.
  • cordova.openwith.WARN for low verbosity, log only warnings and errors.
  • cordova.openwith.ERROR for minimal verbosity, log only errors.

cordova.openwith.addHandler(handlerFunction)

Add an handler function, that will get notified when a file is received.

Handler function

The signature for the handler function is function handlerFunction(intent). See below for what an intent is.

Intent

intent describe the operation to perform, toghether with the associated data. It has the following fields:

  • text: text to share alongside the item, in most cases this will be an empty string.
  • items: an array containing one or more data descriptor.

Data descriptor

A data descriptor describe one file. It is a javascript object with the following fields:

  • uti: Unique Type Identifier. possible values: public.url, public.text or public.image
  • type: Mime type. For example: "image/jpeg"
  • text: test description of the share, generally empty
  • name: suggested file name
  • utis: list of UTIs the file belongs to.

cordova.openwith.load(dataDescriptor, loadSuccessCallback, loadErrorCallback)

Load data for an item. For this modification, it is not necessary,

cordova.openwith.exit()

Attempt to return the the calling app when sharing is done. Your app will be backgrounded, it should be able to finish the upload.

Unfortnately, this is not working on iOS. The user can still select the "Back-to-app" button visible on the top left. Make sure your UI shows the user that he can now safely go back to what he was doing.

Contribute

Contributions in the form of GitHub pull requests are welcome. Please adhere to the following guidelines:

  • Before embarking on a significant change, please create an issue to discuss the proposed change and ensure that it is likely to be merged.
  • Follow the coding conventions used throughout the project. Many conventions are enforced using eslint and pmd. Run npm t to make sure of that.
  • Any contributions must be licensed under the MIT license.

License

MIT

About

Get your Cordova App in the O.S. "Share" menu on iOS and Android

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • JavaScript 35.5%
  • Objective-C 35.5%
  • Java 21.2%
  • Shell 7.0%
  • C 0.8%