This is an automated email from the ASF dual-hosted git repository. github-bot pushed a commit to branch asf-site in repository https://gitbox.apache.org/repos/asf/cordova-docs.git
The following commit(s) were added to refs/heads/asf-site by this push: new ddd1b3cae1 Deploying to asf-site from @ apache/cordova-docs@0000b1aebc9eaaf862c524819f848975cc3b5421 π ddd1b3cae1 is described below commit ddd1b3cae16d51e588efdaee24fc3c01f7de62c8 Author: erisu <er...@users.noreply.github.com> AuthorDate: Mon Jul 7 13:12:51 2025 +0000 Deploying to asf-site from @ apache/cordova-docs@0000b1aebc9eaaf862c524819f848975cc3b5421 π --- docs/en/dev/guide/hybrid/plugins/index.html | 462 ++++++++++------------- docs/en/dev/guide/platforms/ios/plugin.html | 555 ++++++++++++++++++++-------- feed.xml | 4 +- 3 files changed, 603 insertions(+), 418 deletions(-) diff --git a/docs/en/dev/guide/hybrid/plugins/index.html b/docs/en/dev/guide/hybrid/plugins/index.html index 8292756cdd..6dceff4fe5 100644 --- a/docs/en/dev/guide/hybrid/plugins/index.html +++ b/docs/en/dev/guide/hybrid/plugins/index.html @@ -2366,273 +2366,114 @@ <div id="page-toc-source"> <h1>Create a Plugin</h1> -<p>A <em>plugin</em> is a package of injected code that allows the Cordova webview within -which the app renders to communicate with the native platform on -which it runs. Plugins provide access to device and platform -functionality that is ordinarily unavailable to web-based apps. All -the main Cordova API features are implemented as plugins, and many -others are available that enable features such as bar code scanners, -NFC communication, or to tailor calendar interfaces. You can search for available plugins -on <a href="/plugins/">Cordova Plugin Search page</a>.</p> - -<p>Plugins comprise a single JavaScript interface along with -corresponding native code libraries for each supported platform. In essence -this hides the various native code implementations behind a common -JavaScript interface.</p> - -<p>This section steps through a simple <em>echo</em> plugin that passes a string from -JavaScript to the native platform and back, one that you can use as a -model to build far more complex features. This section discusses the -basic plugin structure and the outward-facing JavaScript interface. -For each corresponding native interface, see the list at the end of -this section.</p> - -<p>In addition to these instructions, when preparing to write a plugin it -is best to look over <a href="https://cordova.apache.org/contribute">existing plugins</a> -for guidance.</p> - -<h2>Building a Plugin</h2> - -<p>Application developers use the CLI's <a href="../../../reference/cordova-cli/index.html#cordova-plugin-command">plugin add command</a> to add a plugin to a project. The -command takes the URL for a <em>git</em> repository containing -the plugin code as an argument. This example implements Cordova's Device API:</p> - -<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cordova plugin add https://github.com/apache/cordova-plugin-device -</code></pre></div></div> - -<p>If the plugin is published to <em>npm</em>, the command can also receive the package name as the argument:</p> - -<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cordova plugin add cordova-plugin-device -</code></pre></div></div> - -<p>The plugin repository must feature a top-level <code>plugin.xml</code> manifest -file. There are many ways to configure this file, details for which -are available in the <a href="../../../plugin_ref/spec.html">Plugin Specification</a>.</p> - -<p>This abbreviated version of the <code>Device</code> plugin provides a simple example to use as a model:</p> - -<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?xml version="1.0" encoding="UTF-8"?></span> -<span class="nt"><plugin</span> <span class="na">xmlns=</span><span class="s">"http://apache.org/cordova/ns/plugins/1.0"</span> - <span class="na">id=</span><span class="s">"cordova-plugin-device"</span> <span class="na">version=</span><span class="s">"0.2.3"</span><span class="nt">></span> - <span class="nt"><name></span>Device<span class="nt"></name></span> - <span class="nt"><description></span>Cordova Device Plugin<span class="nt"></description></span> - <span class="nt"><license></span>Apache 2.0<span class="nt"></license></span> - <span class="nt"><keywords></span>cordova,device<span class="nt"></keywords></span> - <span class="nt"><js-module</span> <span class="na">src=</span><span class="s">"www/device.js"</span> <span class="na">name=</span><span class="s">"device"</span><span class="nt">></span> - <span class="nt"><clobbers</span> <span class="na">target=</span><span class="s">"device"</span> <span class="nt">/></span> - <span class="nt"></js-module></span> - <span class="nt"><platform</span> <span class="na">name=</span><span class="s">"ios"</span><span class="nt">></span> - <span class="nt"><config-file</span> <span class="na">target=</span><span class="s">"config.xml"</span> <span class="na">parent=</span><span class="s">"/*"</span><span class="nt">></span> - <span class="nt"><feature</span> <span class="na">name=</span><span class="s">"Device"</span><span class="nt">></span> - <span class="nt"><param</span> <span class="na">name=</span><span class="s">"ios-package"</span> <span class="na">value=</span><span class="s">"CDVDevice"</span><span class="nt">/></span> - <span class="nt"></feature></span> - <span class="nt"></config-file></span> - <span class="nt"><header-file</span> <span class="na">src=</span><span class="s">"src/ios/CDVDevice.h"</span> <span class="nt">/></span> - <span class="nt"><source-file</span> <span class="na">src=</span><span class="s">"src/ios/CDVDevice.m"</span> <span class="nt">/></span> - <span class="nt"></platform></span> -<span class="nt"></plugin></span> -</code></pre></div></div> - -<ul> - <li>The top-level <code>plugin</code> tag's <code>id</code> attribute usually follows the <code>cordova-plugin-{plugin name}</code> schema and matches the plugin's npm package name.</li> - <li>The <code>js-module</code> tag specifies the path to the <a href="#the-javascript-interface">common -JavaScript interface</a>.</li> - <li>The <code>platform</code> tag specifies a corresponding -set of native code, for the <code>ios</code> platform in this case.</li> - <li>The <code>config-file</code> tag encapsulates a <code>feature</code> tag that is injected into -the platform-specific <code>config.xml</code> file to make the platform aware of -the additional code library.</li> - <li>The <code>header-file</code> and <code>source-file</code> tags -specify the path to the library's component files.</li> -</ul> - -<h2>The JavaScript Interface</h2> - -<p>The JavaScript interface provides the front-facing interface, making it perhaps -the most important part of the plugin. You can structure your -plugin's JavaScript however you like, but you need to call -<code>cordova.exec</code> to communicate with the native platform, using the -following syntax:</p> - -<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">cordova</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">winParam</span><span class="p">)</span> <span class="p">{},</span> - <span class="kd">function</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{},</span> - <span class="dl">"</span><span class="s2">service</span><span class="dl">"</span><span class="p">,</span> - <span class="dl">"</span><span class="s2">action</span><span class="dl">"</span><span class="p">,</span> - <span class="p">[</span><span class="dl">"</span><span class="s2">firstArgument</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">secondArgument</span><span class="dl">"</span><span class="p">,</span> <span class="mi">42</span><span class="p">,</span> <span class="kc">false</span><span class="p">]);</span> -</code></pre></div></div> - -<p>Here is how each parameter works:</p> - <ul> - <li> - <p><code>function(winParam) {}</code>: A success callback function. Assuming your -<code>exec</code> call completes successfully, this function executes along -with any parameters you pass to it.</p> - </li> - <li> - <p><code>function(error) {}</code>: An error callback function. If the operation -does not complete successfully, this function executes with an -optional error parameter.</p> - </li> - <li> - <p><code>"service"</code>: The service name to call on the native side. This -corresponds to a native class, for which more information is -available in the native guides listed below.</p> - </li> - <li> - <p><code>"action"</code>: The action name to call on the native side. This -generally corresponds to the native class method. See the native -guides listed below.</p> - </li> - <li> - <p><code>[/* arguments */]</code>: An array of arguments to pass into the native -environment.</p> + <li><a href="#create-a-plugin">Create a Plugin</a> + <ul> + <li><a href="#creating-an-npm-package">Creating an npm Package</a> + <ul> + <li><a href="#updating-the-npm-package-for-cordova">Updating the npm Package for Cordova</a> + <ul> + <li><a href="#adding-the-plugins-id">Adding the plugin's <code>id</code></a></li> + <li><a href="#specifying-supported-platforms">Specifying supported platforms</a></li> + <li><a href="#adding-keywords-for-discoverability">Adding keywords for discoverability</a></li> + <li><a href="#adding-engine-requirements">Adding engine requirements</a> + <ul> + <li><a href="#upper-bounds">Upper Bounds</a></li> + </ul> + </li> + </ul> + </li> + </ul> + </li> + <li><a href="#creating-the-pluginxml-file">Creating the <code>plugin.xml</code> file</a></li> + <li><a href="#adding-a-front-end-javascript-api">Adding a Front-End JavaScript API</a> + <ul> + <li><a href="#cordovaexec-command-syntax"><code>cordova.exec</code> Command Syntax</a></li> + <li><a href="#creating-the-wwwapijs-file">Creating the <code>www/api.js</code> File</a></li> + <li><a href="#injecting-the-javascript-api-to-the-window-object">Injecting the JavaScript API to the <code>window</code> Object</a></li> + </ul> + </li> + <li><a href="#implementing-native-interfaces">Implementing Native Interfaces</a></li> + <li><a href="#testing-a-plugin-during-development">Testing a Plugin during development</a></li> + <li><a href="#publishing-plugins">Publishing Plugins</a></li> + </ul> </li> </ul> -<h2>Sample JavaScript</h2> - -<p>This example shows one way to implement the plugin's JavaScript -interface:</p> +<p>A Cordova <em>plugin</em> is a package that enables the Cordova apps to access native device features and functionality that is ordinarily unavailable to web-based apps. All of the core Cordova API features are implemented as plugins. Many third-party plugins are also available to provide additional capabilities such as barcode scanning, near-field communication (NFC), push notification, or even customizing interfaces.</p> -<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">window</span><span class="p">.</span><span class="nx">echo</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">str</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span> - <span class="nx">cordova</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="nx">callback</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> - <span class="nx">callback</span><span class="p">(</span><span class="dl">'</span><span class="s1">Nothing to echo.</span><span class="dl">'</span><span class="p">);</span> - <span class="p">},</span> <span class="dl">"</span><span class="s2">Echo</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">echo</span><span class="dl">"</span><span class="p">,</span> <span class="p">[</span><span class="nx">str</span><span class="p">]);</span> -<span class="p">};</span> -</code></pre></div></div> - -<p>In this example, the plugin attaches itself to the <code>window</code> object as -the <code>echo</code> function, which plugin users would call as follows:</p> - -<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">window</span><span class="p">.</span><span class="nx">echo</span><span class="p">(</span><span class="dl">"</span><span class="s2">echome</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">echoValue</span><span class="p">)</span> <span class="p">{</span> - <span class="nx">alert</span><span class="p">(</span><span class="nx">echoValue</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">echome</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// should alert true.</span> -<span class="p">});</span> -</code></pre></div></div> - -<p>Look at the last three arguments passed to the <code>cordova.exec</code> function. The -first calls the <code>Echo</code> <em>service</em>, a class name. The second requests -the <code>echo</code> <em>action</em>, a method within that class. The third is an array -of arguments containing the echo string, which is the <code>window.echo</code> -function's first parameter.</p> - -<p>The success callback passed into <code>exec</code> is simply a reference to the -callback function of <code>window.echo</code>. If the native platform fires -the error callback, it simply calls the success callback and passes it -a default string.</p> - -<h2>Native Interfaces</h2> - -<p>Once you define JavaScript for your plugin, you need to complement it -with at least one native implementation. Details for each platform are -listed below, and each builds on the simple Echo Plugin example above:</p> +<p>Check out these locations for Cordova plugins:</p> <ul> - <li><a href="../../platforms/android/plugin.html">Android Plugins</a></li> - <li><a href="../../platforms/ios/plugin.html">iOS Plugins</a></li> + <li>Official Apache Cordova plugins on the <a href="/plugins/">Cordova Plugin page</a>.</li> + <li>Third-party plugins on the <a href="https://www.npmjs.com/search?q=keywords:ecosystem:cordova">npmjs registry</a>.</li> </ul> -<h2>Testing a Plugin during development</h2> +<p>Plugins usually consist of a JavaScript interface paired with corresponding platform-native code. In essence, this hides the platform-specific native implementations behind a common JavaScript interface.</p> -<p>The simplest way to manually test a plugin during development is to create a -Cordova app as usual and add the plugin with the <code>--link</code> option:</p> +<p>This page will walk through the steps to create a basic <em>echo</em> plugin that passes a string from the front-end JavaScript to the native platform and back. The purpose of this guide is to provide a model for how to build and publish a Cordova plugin. It focuses on the fundamentals of plugin structure and the outward-facing JavaScript interface.</p> -<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cordova plugin add ../path/to/my/plugin/relative/to/project <span class="nt">--link</span> -</code></pre></div></div> +<p>For the corresponding native implementations, see the list at the end of this section.</p> -<p>This creates a symbolic link instead of copying the plugin files, which enables you to work on your plugin and then simply rebuild the app to use your changes. The plugin should be added after the platform, or the link will not work. The link will also be lost if you re-add the platform or <a href="../../../platform_plugin_versioning_ref/index.md">restore the project</a> with <code>cordova prepare</code>. In that case, you'll need to re-add the plugin to restore the link.</p> +<p>In addition to following these instructions, it is recommended to review <a href="https://cordova.apache.org/contribute">existing plugins</a> for further guidance.</p> -<h2>Validating a Plugin using Plugman</h2> +<h2>Creating an npm Package</h2> -<p>You can use the <code>plugman</code> utility to check whether the plugin installs -correctly for each platform. Install <code>plugman</code> with the following -<a href="https://nodejs.org/">node</a> command:</p> +<p>In essence, a Cordova plugin is an extension of an npm package. By leveraging the npm ecosystem, plugin developers can easily publish their plugins to the npm registry, allowing app developers to install them into their Cordova apps using the Cordova CLI.</p> -<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <span class="nb">install</span> <span class="nt">-g</span> plugman -</code></pre></div></div> - -<p>You need a valid app source directory, such as the top-level <code>www</code> -directory included in a default CLI-generated project, as described in the -<a href="../../cli/index.html">Create your first app</a> guide.</p> +<p>Even if you don't plan to publish your plugin publicly, you still need to structure it as an npm package for installation purposes. The <a href="../../../reference/cordova-cli/index.html#cordova-plugin-command"><code>cordova plugin add</code></a> command relies on npm under the hood to fetch and install plugins.</p> -<p>Then run a command such as the following to test whether iOS -dependencies load properly:</p> +<p>First, we'll create the directory for our <code>echo</code> plugin and change to this newly created directory:</p> -<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>plugman <span class="nb">install</span> <span class="nt">--platform</span> ios <span class="nt">--project</span> /path/to/my/project/www <span class="nt">--plugin</span> /path/to/my/plugin +<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir </span>cordova-plugin-echo +<span class="nb">cd </span>cordova-plugin-echo </code></pre></div></div> -<p>For details on <code>plugman</code> options, see <a href="../../../plugin_ref/plugman.html">Using Plugman to Manage Plugins</a>. For information on how to actually <em>debug</em> plugins, see <a href="#native-interfaces">each platform's native interface listed above</a>.</p> +<p>Next, we'll initialize it as an npm package using the <code>npm init</code> command. In this example, we'll accept all default values for the initialization process by appending the <code>-y</code> flag. If you want to customize the values, you can omit the flag or change the values later by editing the <code>package.json</code> file.</p> -<h2>Publishing Plugins</h2> +<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm init <span class="nt">-y</span> +</code></pre></div></div> -<p>You can publish your plugin to any <code>npmjs</code>-based registry, but the recommended one is the <a href="https://www.npmjs.com">npm registry</a>. Other developers can install your plugin automatically using either <code>plugman</code> or the Cordova CLI.</p> +<p><strong>One important note:</strong> the directory name <code>cordova-plugin-echo</code> will be used as the default package name and will be published as such to the npm registry. If the name is already taken, you'll need to choose a different name or use <a href="https://docs.npmjs.com/cli/v10/using-npm/scope">scoped packages</a>.</p> -<p>To publish a plugin to npm you need to follow these steps:</p> +<h3>Updating the npm Package for Cordova</h3> -<ul> - <li> - <p>install the <code>plugman</code> CLI:</p> +<p>In addition to the standard properties that the <code>package.json</code> file includes for npm, Cordova-specific properties will also be added. These are necessary to define the plugin's ID, supported platforms, relevant keywords for discoverability, and engine requirements.</p> - <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm <span class="nb">install</span> <span class="nt">-g</span> plugman -</code></pre></div> </div> - </li> - <li> - <p>create a <code>package.json</code> file for your plugin:</p> +<h4>Adding the plugin's <code>id</code></h4> - <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>plugman createpackagejson /path/to/your/plugin -</code></pre></div> </div> - </li> - <li> - <p>publish it:</p> +<p>This uniquely identifies your plugin within the Cordova ecosystem. It's generally recommended to match the plugin ID with the npm package name so that when a Cordova project is restored using the <code>cordova prepare</code> command, the package can be easily located in the npm registry.</p> - <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm adduser <span class="c"># that is if you don't have an account yet</span> -<span class="nv">$ </span>npm publish /path/to/your/plugin -</code></pre></div> </div> - </li> -</ul> +<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm pkg <span class="nb">set </span>cordova.id<span class="o">=</span>cordova-plugin-echo +</code></pre></div></div> -<p>For more details on npm usage, refer to <a href="https://docs.npmjs.com/getting-started/publishing-npm-packages">Publishing npm Packages</a> on the npm documentation site.</p> +<h4>Specifying supported platforms</h4> -<h2>Integrating with Plugin Search</h2> +<p>The following example shows how to add support for both Android and iOS. You can modify this to include only the platforms your plugin supports.</p> -<p>To surface the plugin in <a href="/plugins/">Cordova Plugin Search</a>, add the <code>ecosystem:cordova</code> keyword to the <code>package.json</code> file of your plugin before publishing.</p> +<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm pkg <span class="nb">set</span> <span class="s2">"cordova.platforms[]=android"</span> +npm pkg <span class="nb">set</span> <span class="s2">"cordova.platforms[]=ios"</span> +</code></pre></div></div> -<p>To indicate support for a particular platform, add a keyword in the format <code>cordova-<platformName></code> to the list of keywords in <code>package.json</code>. -Plugman's <code>createpackagejson</code> command does this for you, but if you did not use it to generate your <code>package.json</code>, you should manually edit it as shown below.</p> +<h4>Adding keywords for discoverability</h4> -<p>For example, for a plugin that supports Android & iOS the keywords in <code>package.json</code> should include:</p> +<p>Keywords help others find your plugin via search.</p> -<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"keywords"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> - </span><span class="s2">"ecosystem:cordova"</span><span class="p">,</span><span class="w"> - </span><span class="s2">"cordova-android"</span><span class="p">,</span><span class="w"> - </span><span class="s2">"cordova-ios"</span><span class="w"> -</span><span class="p">]</span><span class="w"> -</span></code></pre></div></div> +<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm pkg <span class="nb">set</span> <span class="s2">"keywords[]=cordova"</span> +npm pkg <span class="nb">set</span> <span class="s2">"keywords[]=echosystem:cordova"</span> +npm pkg <span class="nb">set</span> <span class="s2">"keywords[]=cordova-android"</span> +npm pkg <span class="nb">set</span> <span class="s2">"keywords[]=cordova-ios"</span> +</code></pre></div></div> -<p>For a more detailed example of a package.json, review the <a href="https://github.com/apache/cordova-plugin-device/blob/master/package.json">package.json file of cordova-plugin-device</a>.</p> +<h4>Adding engine requirements</h4> -<h2>Specifying Cordova Dependencies</h2> +<p><strong>Cordova 6.1.0</strong> added support for specifying the Cordova-related dependencies of a plugin as part of the plugin's <code>package.json</code> file. Plugins may list the dependencies for multiple releases to provide guidance to the Cordova CLI when it is selecting the version of a plugin to fetch from npm. The CLI will choose the latest release of a plugin that is compatible with the local project's installed platforms and plugins as well as the the local Cordova C [...] -<p><strong>Cordova 6.1.0</strong> added support for specifying the Cordova-related dependencies of a plugin -as part of the plugin's <code>package.json</code> file. Plugins may list the dependencies for multiple -releases to provide guidance to the Cordova CLI when it is selecting the version of a -plugin to fetch from npm. The CLI will choose the latest release of a plugin that is -compatible with the local project's installed platforms and plugins as well as the -the local Cordova CLI version. If no releases of the plugin are compatible, the CLI will warn -the user about the failed requirements and fall back to the old behavior of fetching the -latest release.</p> +<p>This feature is intended to eventually replace the <a href="../../../plugin_ref/spec.html#engines-and-engine">engines element</a> in <code>plugin.xml</code>.</p> -<p>This feature is intended to eventually replace the <a href="../../../plugin_ref/spec.html#engines-and-engine">engines element</a> in plugin.xml. -Listing dependencies is a good way to ensure that your plugin will not appear broken or cause -build errors when fetched from npm. If the latest release of the plugin is not compatible with -a project, the CLI will give the app developer a list of unmet project requirements so that -they are aware of incompatibilites and can update their project to support your plugin. This -allows your plugin to respond to breaking changes without fear of confusing devlopers who -are building against old platforms and plugins.</p> +<p>Listing dependencies is a good way to ensure that your plugin will not appear broken or cause build errors when fetched from npm. If the latest release of the plugin is not compatible with a project, the CLI will give the app developer a list of unmet project requirements so that they are aware of incompatibilites and can update their project to support your plugin. This allows your plugin to respond to breaking changes without fear of confusing devlopers who are building against old [...] -<p>To specify Cordova-related dependencies for a plugin, alter the <code>engines</code> element in -<code>package.json</code> to include a <code>cordovaDependencies</code> object with the following -structure:</p> +<p>To specify Cordova-related dependencies for a plugin, alter the <code>engines</code> element in <code>package.json</code> to include a <code>cordovaDependencies</code> object with the following structure:</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">"</span><span class="s2">engines</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span> <span class="dl">"</span><span class="s2">cordovaDependencies</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span> @@ -2658,14 +2499,9 @@ structure:</p> <li><code>SEMVER_RANGE</code> should adhere to the syntax for a range as defined by <a href="https://www.npmjs.com/package/semver">npm's semver package</a></li> </ul> -<p><strong>NOTE:</strong> A Cordova platform <code>DEPENDENCY</code> refers to the Cordova platform and not -the OS, i.e. <code>cordova-android</code> rather than the Android OS.</p> +<p><strong>NOTE:</strong> A Cordova platform <code>DEPENDENCY</code> refers to the Cordova platform and not the OS, i.e. <code>cordova-android</code> rather than the Android OS.</p> -<p>Your <code>cordovaDependencies</code> may list any number of <code>PLUGIN_VERSION</code> requirements -and any number of <code>DEPENDENCY</code> constraints. Versions of your plugin -that do not have their dependencies listed will be assumed to have the same -dependency information as the highest <code>PLUGIN_VERSION</code> listed below them. For -example, consider the following entry:</p> +<p>Your <code>cordovaDependencies</code> may list any number of <code>PLUGIN_VERSION</code> requirements and any number of <code>DEPENDENCY</code> constraints. Versions of your plugin that do not have their dependencies listed will be assumed to have the same dependency information as the highest <code>PLUGIN_VERSION</code> listed below them. For example, consider the following entry:</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">"</span><span class="s2">engines</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span> <span class="dl">"</span><span class="s2">cordovaDependencies</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span> @@ -2674,22 +2510,12 @@ example, consider the following entry:</p> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> -<p>All plugin versions below the lowest entry (1.0.0 in this example) are assumed -to have no dependencies. Any version of the plugin between 1.0.0 and 2.1.0 is -assumed to have the same dependencies as version 1.0.0 (a cordova-android -version less than 3.0.0). This lets you only update your <code>cordovaDependencies</code> +<p>All plugin versions below the lowest entry (1.0.0 in this example) are assumed to have no dependencies. Any version of the plugin between 1.0.0 and 2.1.0 is assumed to have the same dependencies as version 1.0.0 (a cordova-android version less than 3.0.0). This lets you only update your <code>cordovaDependencies</code> information when there are breaking changes.</p> -<h3>Upper Bounds</h3> +<h5>Upper Bounds</h5> -<p>In addition to a single version, a <code>PLUGIN_VERSION</code> in <code>cordovaDependencies</code> -may also specify an upper bound to amend entries for older releases -of your plugin. This is useful when a breaking change occurs in a <code>DEPENDENCY</code> -and a new constraint must be added for all older versions of a plugin that do -not support it. These bounds should be written as a <code><</code> followed by a single -<a href="https://www.npmjs.com/package/semver">semver</a> version (<strong>Not an arbitrary range!</strong>). This will apply -whatever <code>DEPENDENCY</code> values are given to all versions of the plugin below the -specified version. For example, consider the following entry:</p> +<p>In addition to a single version, a <code>PLUGIN_VERSION</code> in <code>cordovaDependencies</code> may also specify an upper bound to amend entries for older releases of your plugin. This is useful when a breaking change occurs in a <code>DEPENDENCY</code> and a new constraint must be added for all older versions of a plugin that do not support it. These bounds should be written as a <code><</code> followed by a single <a href="https://www.npmjs.com/package/semver">semver</a> versi [...] <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">"</span><span class="s2">engines</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span> <span class="dl">"</span><span class="s2">cordovaDependencies</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span> @@ -2700,17 +2526,137 @@ specified version. For example, consider the following entry:</p> <span class="p">}</span> </code></pre></div></div> -<p>Here we specify one plugin version (0.0.1) and two upper bounds (<1.0.0 and <2.0.0) -that constrain cordova-ios. The two upper bounds do not override the constraint -of 0.0.1, they are combined via AND at evaluation time. When the CLI checks the -cordova-ios version of the project, the constraint that will be evaluated for -plugin version 0.0.1 will be the combination of these three:</p> +<p>Here we specify one plugin version (0.0.1) and two upper bounds (<1.0.0 and <2.0.0) that constrain cordova-ios. The two upper bounds do not override the constraint of 0.0.1, they are combined via AND at evaluation time. When the CLI checks the cordova-ios version of the project, the constraint that will be evaluated for plugin version 0.0.1 will be the combination of these three:</p> + +<blockquote> + <p>cordova-ios >1.0.0 AND cordova-ios <2.0.0 AND cordova-ios <5.0.0</p> +</blockquote> + +<p>Please note that the only <code>PLUGIN_VERSION</code> values allowed are single versions or upper bounds; no other semver ranges are supported.</p> + +<h2>Creating the <code>plugin.xml</code> file</h2> + +<p>Plugins must also be paired with a top-level <code>plugin.xml</code> manifest file. This file is used for configuring the plugin. See the <a href="../../../plugin_ref/spec.html">Plugin.xml Specification</a> for more information of the elements that can be defined.</p> + +<p>Below is a simple example of the <code>plugin.xml</code> file that will be used for the <code>Echo</code> plugin and a model to follow for creating your own plugins.</p> + +<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?xml version="1.0" encoding="utf-8"?></span> +<span class="nt"><plugin</span> <span class="na">xmlns=</span><span class="s">"http://apache.org/cordova/ns/plugins/1.0"</span> + <span class="na">id=</span><span class="s">"cordova-plugin-echo"</span> + <span class="na">version=</span><span class="s">"1.0.0"</span><span class="nt">></span> + <span class="nt"><name></span>Echo<span class="nt"></name></span> + <span class="nt"><description></span>Cordova Echo Plugin<span class="nt"></description></span> + <span class="nt"><license></span>Apache 2.0<span class="nt"></license></span> + <span class="nt"><keywords></span>cordova,plugin,echo<span class="nt"></keywords></span> +<span class="nt"></plugin></span> +</code></pre></div></div> + +<blockquote> + <p>Note: The top-level <code>plugin</code> tag's <code>id</code> attribute usually follows the <code>cordova-plugin-{plugin name}</code> schema and matches the plugin's npm package name.</p> +</blockquote> + +<h2>Adding a Front-End JavaScript API</h2> + +<p>Plugin developers typically include a front-end JavaScript API. The primary purpose is to abstract away Cordova's internal APIs, eliminate the need for app developers to understand the specific naming of your plugin's service or methods, and simplify the overall usage of the plugin.</p> + +<h3><code>cordova.exec</code> Command Syntax</h3> + +<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">exec</span><span class="p">(</span><span class="o"><</span><span class="nx">successFunction</span><span class="o">></span><span class="p">,</span> <span class="o"><</span><span class="nx">failFunction</span><span class="o">></span><span class="p">,</span> <span class="o"><</span><span class="nx">service</span><span class="o">></span><span class="p">,</ [...] +</code></pre></div></div> + +<p>The <code>cordova.exec</code> method is what triggeres the request from the front-end WebView to the native side of the platform, invoking an action method on the specified service class with the provided arguments. Depending on the outcome, either the success or failure callback will be triggered.</p> + +<ul> + <li><strong><code>successCallback</code></strong>: The first argument is the success callback. If the native operation completes successfully, this function is invoked with any returned data.</li> + <li><strong><code>failCallback</code></strong>: The second argument is the error callback. If the operation fails, this function is called with an error object or message.</li> + <li><strong><code>service</code></strong>: A string representing the service name on the native side. This typically matches a native class defined in your plugin.</li> + <li><strong><code>action</code></strong>: A string representing the method name to invoke on the native service.</li> + <li><strong><code>[arguments]</code></strong>: An array of arguments to be passed to the native method.</li> +</ul> + +<h3>Creating the <code>www/api.js</code> File</h3> + +<p>In this example, we'll create a folder named <code>www</code> in the plugin's root directory and add a file named <code>api.js</code>.</p> + +<blockquote> + <p>Note: The directory and file names are customizable and do not need to follow this exact structure. However, if you choose to rename the directory or file, be sure to update the corresponding <code>js-module</code> path in the <code>plugin.xml</code> file accordingly.</p> +</blockquote> + +<p>The <code>www/api.js</code> file will contain the front-end JavaScript API. For this example, the contents of the Echo plugin will be as follows:</p> + +<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">exec</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">cordova/exec</span><span class="dl">'</span><span class="p">);</span> +<span class="kd">const</span> <span class="nx">serviceName</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">Echo</span><span class="dl">'</span><span class="p">;</span> + +<span class="kd">const</span> <span class="nx">Echo</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="p">};</span> + +<span class="nx">Echo</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">echo</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">message</span><span class="p">)</span> <span class="p">{</span> + <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span> + <span class="kd">const</span> <span class="nx">_successCb</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">result</span><span class="p">)</span> <span class="p">{</span> + <span class="nx">resolve</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span> + <span class="p">};</span> + + <span class="kd">const</span> <span class="nx">_errorCb</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> + <span class="nx">reject</span><span class="p">(</span><span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="nx">err</span><span class="p">));</span> + <span class="p">};</span> + + <span class="nx">exec</span><span class="p">(</span><span class="nx">_successCb</span><span class="p">,</span> <span class="nx">_errorCb</span><span class="p">,</span> <span class="nx">serviceName</span><span class="p">,</span> <span class="dl">'</span><span class="s1">echo</span><span class="dl">'</span><span class="p">,</span> <span class="p">[</span><span class="nx">message</span><span class="p">]);</span> + <span class="p">});</span> +<span class="p">};</span> + +<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Echo</span><span class="p">();</span> +</code></pre></div></div> + +<p>This example demonstrates how to build a front-end plugin API that returns a Promise, offering a modern and user-friendly interface for app developers.</p> + +<h3>Injecting the JavaScript API to the <code>window</code> Object</h3> + +<p>To make the Echo JavaScript API available on the WebView's <code>window</code> object, we need to update the <code>plugin.xml</code> to add the injection our API using the <code><js-module></code> element.</p> + +<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><js-module</span> <span class="na">src=</span><span class="s">"www/api.js"</span> <span class="na">name=</span><span class="s">"Echo"</span><span class="nt">></span> + <span class="nt"><clobbers</span> <span class="na">target=</span><span class="s">"Echo"</span> <span class="nt">/></span> +<span class="nt"></js-module></span> +</code></pre></div></div> + +<p>The above wil take the <code>www/api.js</code> and clobber it onto the <code>window</code> object as <code>window.Echo</code>.</p> + +<p>Usually, when supporting multiple platforms, all platforms has the same JavaScript API. In this case, the above XML does not need to be posted inside the <code><platform></code> element. If there was a case where each platform has their own own JavaScript file, then the <code><js-module></code> should be added to the <code><platform></code> element.</p> + +<h2>Implementing Native Interfaces</h2> + +<p>Once you created the core structure of the plugin with the above section, we can how complement it with at least one native implementation.</p> + +<p>Details for each platform are listed below, and each section is a continuation of the simple Echo Plugin:</p> + +<ul> + <li><a href="../../platforms/android/plugin.html">Android Plugin Development Guide</a></li> + <li><a href="../../platforms/ios/plugin.html">iOS Plugin Development Guide</a></li> +</ul> + +<h2>Testing a Plugin during development</h2> + +<p>Usually, the simplest way to manually test a plugin during development is to create a Cordova app and add the plugin with the <code>--link</code> option:</p> + +<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cordova plugin add ../path/to/my/plugin/relative/to/project <span class="nt">--link</span> +</code></pre></div></div> + +<p>This will creates a symbolic link instead of copying the plugin files, which enables you to work on your plugin and then simply rebuild the app to use your changes. The plugin should be added after the platform, or the link will not work. The link will also be lost if you re-add the platform or <a href="../../../platform_plugin_versioning_ref/index.md">restore the project</a> with <code>cordova prepare</code>. In that case, you'll need to re-add the plugin to restore the link.</p> + +<h2>Publishing Plugins</h2> + +<p>You can publish your plugin to any <code>npmjs</code>-based registry, but the recommended one is the <a href="https://www.npmjs.com">npm registry</a>. This allows other developers to easily install your plugin using the Cordova CLI.</p> + +<p>To publish,</p> + +<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm adduser <span class="c"># that is if you don't have an account yet</span> +<span class="nv">$ </span>npm publish /path/to/your/plugin +</code></pre></div></div> + +<p>If you do not plan to publish your plugin publicly, it is recommended to set the <code>private</code> flag in the <code>package.json</code> to <code>true</code> to prevent accidental publication.</p> -<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> cordova-ios >1.0.0 AND cordova-ios <2.0.0 AND cordova-ios <5.0.0 +<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm pkg <span class="nb">set </span><span class="nv">private</span><span class="o">=</span><span class="nb">true</span> <span class="nt">--json</span> </code></pre></div></div> -<p>Please note that the only <code>PLUGIN_VERSION</code> values allowed are single versions or -upper bounds; no other semver ranges are supported.</p> +<p>For more details on npm usage, refer to <a href="https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry">Contributing packages to the registry</a> on the npm documentation site.</p> diff --git a/docs/en/dev/guide/platforms/ios/plugin.html b/docs/en/dev/guide/platforms/ios/plugin.html index 779995865d..07ab937b38 100644 --- a/docs/en/dev/guide/platforms/ios/plugin.html +++ b/docs/en/dev/guide/platforms/ios/plugin.html @@ -2368,134 +2368,207 @@ <div id="page-toc-source"> <h1>iOS Plugin Development Guide</h1> -<p>This section provides details for how to implement native plugin code -on the iOS platform.</p> +<ul> + <li><a href="#ios-plugin-development-guide">iOS Plugin Development Guide</a> + <ul> + <li><a href="#creating-an-cordova-plugin-for-ios">Creating an Cordova Plugin for iOS</a> + <ul> + <li><a href="#adding-native-source-code">Adding Native Source Code</a></li> + <li><a href="#configuring-the-pluginxml">Configuring the <code>plugin.xml</code></a> + <ul> + <li><a href="#adding-plugin-code-to-ios-project">Adding Plugin Code to iOS Project</a></li> + <li><a href="#setting-class-mapping-for-webview-to-native-communication">Setting Class Mapping for WebView-to-Native Communication</a></li> + <li><a href="#configuring-plugin-initialization-timing">Configuring Plugin Initialization Timing</a></li> + </ul> + </li> + <li><a href="#supporting-swift-package-manager-spm">Supporting Swift Package Manager (SPM)</a> + <ul> + <li><a href="#creating-spms-packageswift-file">Creating SPM's <code>Package.swift</code> File</a></li> + </ul> + </li> + <li><a href="#additional-native-side-implementation">Additional Native Side Implementation</a> + <ul> + <li><a href="#executing-plugin-initialization-logic">Executing Plugin Initialization Logic</a></li> + <li><a href="#handeling-long-running--background-activities">Handeling Long-running \& Background Activities</a></li> + <li><a href="#hooking-into-wkurlschemetask">Hooking into WKURLSchemeTask</a></li> + <li><a href="#using-background-threads">Using Background Threads</a></li> + <li><a href="#adding-a-privacy-manifest-file">Adding a Privacy Manifest File</a></li> + </ul> + </li> + </ul> + </li> + <li><a href="#cdvpluginresult-message-types">CDVPluginResult Message Types</a></li> + <li><a href="#other-supported-cdvplugin-features">Other Supported <code>CDVPlugin</code> Features</a></li> + <li><a href="#debugging-plugins-for-ios">Debugging Plugins for iOS</a></li> + <li><a href="#common-pitfalls">Common Pitfalls</a></li> + </ul> + </li> +</ul> -<p>Before reading this, see <a href="../../hybrid/plugins/index.html">Plugin Development Guide</a> for -an overview of the plugin's structure and its common JavaScript -interface. This section continues to demonstrate the sample <em>echo</em> -plugin that communicates from the Cordova webview to the native -platform and back.</p> +<p>This guide provides details on implementing native plugin code for the iOS platform. The plugin's platform-native code can be written in either Objective-C or Swift.</p> -<p>An iOS plugin is implemented as an Objective-C class that extends the -<code>CDVPlugin</code> class. For JavaScript's <code>exec</code> method's <code>service</code> -parameter to map to an Objective-C class, each plugin class must be -registered as a <code><feature></code> tag in the named application directory's -<code>config.xml</code> file.</p> +<p>Before proceeding, refer to the <a href="../../hybrid/plugins/index.html">Plugin Development Guide</a> for an overview of plugin structure, plugin core files, and its common JavaScript interface. This guide will continue to use the <em>echo</em> plugin, as an exmaple, which enables communication between the Cordova WebView and the native platform.</p> -<h2>Plugin Class Mapping</h2> +<h2>Creating an Cordova Plugin for iOS</h2> -<p>The JavaScript portion of a plugin uses the <code>cordova.exec</code> method as -follows:</p> +<p>In this section we will cover:</p> -<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">exec</span><span class="p">(</span><span class="o"><</span><span class="nx">successFunction</span><span class="o">></span><span class="p">,</span> <span class="o"><</span><span class="nx">failFunction</span><span class="o">></span><span class="p">,</span> <span class="o"><</span><span class="nx">service</span><span class="o">></span><span class="p">,</ [...] -</code></pre></div></div> +<ol> + <li>Adding Native Source Code</li> + <li>Configuring <code>plugin.xml</code> + <ul> + <li>Adding Plugin Code to iOS Project</li> + <li>Setting Class Mapping for WebView-to-Native Communication</li> + </ul> + </li> + <li>Adding Swift Package Manager Support</li> + <li>Additional Native Side Implementation</li> +</ol> -<p>This marshals a request from the <code>UIWebView</code> to the iOS native side, -effectively calling the <code>action</code> method on the <code>service</code> class, with -the arguments passed in the <code>args</code> array.</p> +<h3>Adding Native Source Code</h3> -<p>Specify the plugin as a <code><feature></code> tag in your Cordova-iOS -application's project's <code>config.xml</code> file, using the <code>plugin.xml</code> file -to inject this markup automatically, as described in <a href="../../hybrid/plugins/index.html">Plugin Development Guide</a>:</p> +<p>In the following example, we will place all files in the <code>src/ios/</code> directory. This directory will be located inside the Cordova plugin's project root directory. The name and path of the directory are not strict and can be customized as you prefer. However, this is the typical pattern used by official Apache Cordova plugins to separate platform-specific source code and resources.</p> -<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><feature</span> <span class="na">name=</span><span class="s">"LocalStorage"</span><span class="nt">></span> - <span class="nt"><param</span> <span class="na">name=</span><span class="s">"ios-package"</span> <span class="na">value=</span><span class="s">"CDVLocalStorage"</span> <span class="nt">/></span> -<span class="nt"></feature></span> -</code></pre></div></div> +<ul> + <li> + <p><strong>Swift</strong></p> -<p>The feature's <code>name</code> attribute should match what you specify as the -JavaScript <code>exec</code> call's <code>service</code> parameter. The <code>value</code> attribute -should match the name of the plugin's Objective-C class. The <code><param></code> -element's <code>name</code> should always be <code>ios-package</code>. If you do not follow -these guidelines, the plugin may compile, but Cordova may still not be -able to access it.</p> + <p>In Swift, the implementation source code is written inside a <code>.swift</code> file. This is where the business logic is performed.</p> -<h2>Plugin Initialization and Lifetime</h2> + <p>To expose methods written in Swift to Objective-C, the <code>@objc</code> annotation needs to be added. When the <code>@objc</code> annotation is used, those methods are automatically included in the <code>-Swift.h</code> header file. This is required so that Cordova can locate and invoke them.</p> -<p>One instance of a plugin object is created for the life of each -<code>UIWebView</code>. Plugins are not instantiated until they are first -referenced by a call from JavaScript, unless <code><param></code> with an <code>onload</code> -<code>name</code> attribute is set to <code>"true"</code> in <code>config.xml</code>. For example,</p> + <p><strong>Echo.swift (Source File):</strong></p> -<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><feature</span> <span class="na">name=</span><span class="s">"Echo"</span><span class="nt">></span> - <span class="nt"><param</span> <span class="na">name=</span><span class="s">"ios-package"</span> <span class="na">value=</span><span class="s">"Echo"</span> <span class="nt">/></span> - <span class="nt"><param</span> <span class="na">name=</span><span class="s">"onload"</span> <span class="na">value=</span><span class="s">"true"</span> <span class="nt">/></span> -<span class="nt"></feature></span> -</code></pre></div></div> + <p>In this example, when the <code>echo</code> method is invoked, an <code>.ok</code> response with the provided message is returned if the message exists; otherwise, an <code>.error</code> is returned.</p> -<p>Plugins should use the <code>pluginInitialize</code> method for their startup logic.</p> + <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="cp">#if canImport(Cordova)</span> + <span class="kd">import</span> <span class="kt">Cordova</span> + <span class="cp">#endif</span> -<p>Plugins with long-running requests or background activities such as media -playback, listeners, or that maintain internal state should implement -the <code>onReset</code> method to cancel those long-running requests or to clean up -after those activities. -The method runs when the <code>UIWebView</code> navigates to a new page or refreshes, which -reloads the JavaScript.</p> + <span class="kd">@objc</span><span class="p">(</span><span class="kt">Echo</span><span class="p">)</span> + <span class="kd">class</span> <span class="kt">Echo</span> <span class="p">:</span> <span class="kt">CDVPlugin</span> <span class="p">{</span> + <span class="kd">@objc</span> <span class="kd">func</span> <span class="nf">sample</span><span class="p">(</span><span class="n">_</span> <span class="nv">command</span> <span class="p">:</span> <span class="kt">CDVInvokedUrlCommand</span><span class="p">)</span> <span class="p">{</span> + <span class="k">let</span> <span class="nv">myarg</span> <span class="o">=</span> <span class="n">command</span><span class="o">.</span><span class="n">arguments</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> + <span class="k">let</span> <span class="nv">pluginResult</span><span class="p">;</span> -<h2>Writing an iOS Cordova Plugin</h2> + <span class="k">if</span> <span class="p">(</span><span class="n">myarg</span> <span class="o">!=</span> <span class="kc">nil</span><span class="p">)</span> <span class="p">{</span> + <span class="n">pluginResult</span> <span class="o">=</span> <span class="kt">CDVPluginResult</span><span class="p">(</span><span class="nv">status</span><span class="p">:</span> <span class="o">.</span><span class="n">ok</span><span class="p">,</span> <span class="nv">messageAs</span><span class="p">:</span> <span class="n">myarg</span><span class="p">)</span> + <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> + <span class="n">pluginResult</span> <span class="o">=</span> <span class="kt">CDVPluginResult</span><span class="p">(</span><span class="nv">status</span><span class="p">:</span> <span class="o">.</span><span class="n">error</span><span class="p">)</span> + <span class="p">}</span> -<p>A JavaScript call fires off a plugin request to the native side, and -the corresponding iOS Objective-C plugin is mapped properly in the -<code>config.xml</code> file, but what does the final iOS Objective-C plugin -class look like? Whatever is dispatched to the plugin with -JavaScript's <code>exec</code> function is passed into the corresponding plugin -class's <code>action</code> method. A plugin method has this signature:</p> + <span class="k">self</span><span class="o">.</span><span class="n">commandDelegate</span><span class="o">.</span><span class="nf">send</span><span class="p">(</span><span class="n">pluginResult</span><span class="p">,</span> <span class="nv">callbackId</span><span class="p">:</span> <span class="n">command</span><span class="o">.</span><span class="n">callbackId</span><span class="p">)</span> + <span class="p">}</span> + <span class="p">}</span> +</code></pre></div> </div> + </li> + <li> + <p><strong>Objective-C</strong></p> -<div class="language-objective_c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">myMethod</span><span class="p">:(</span><span class="n">CDVInvokedUrlCommand</span><span class="o">*</span><span class="p">)</span><span class="nv">command</span> -<span class="p">{</span> - <span class="n">CDVPluginResult</span><span class="o">*</span> <span class="n">pluginResult</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span> - <span class="n">NSString</span><span class="o">*</span> <span class="n">myarg</span> <span class="o">=</span> <span class="p">[</span><span class="n">command</span><span class="p">.</span><span class="n">arguments</span> <span class="nf">objectAtIndex</span><span class="p">:</span><span class="mi">0</span><span class="p">];</span> - - <span class="k">if</span> <span class="p">(</span><span class="n">myarg</span> <span class="o">!=</span> <span class="nb">nil</span><span class="p">)</span> <span class="p">{</span> - <span class="n">pluginResult</span> <span class="o">=</span> <span class="p">[</span><span class="n">CDVPluginResult</span> <span class="nf">resultWithStatus</span><span class="p">:</span><span class="n">CDVCommandStatus_OK</span><span class="p">];</span> - <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> - <span class="n">pluginResult</span> <span class="o">=</span> <span class="p">[</span><span class="n">CDVPluginResult</span> <span class="nf">resultWithStatus</span><span class="p">:</span><span class="n">CDVCommandStatus_ERROR</span> <span class="nf">messageAsString</span><span class="p">:</span><span class="s">@"Arg was null"</span><span class="p">];</span> - <span class="p">}</span> - <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">commandDelegate</span> <span class="nf">sendPluginResult</span><span class="p">:</span><span class="n">pluginResult</span> <span class="nf">callbackId</span><span class="p">:</span><span class="n">command</span><span class="p">.</span><span class="n">callbackId</span><span class="p">];</span> -<span class="p">}</span> -</code></pre></div></div> + <p><strong>Echo.h (Header File):</strong></p> -<p>For more details, see - <a href="https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/Public/CDVInvokedUrlCommand.h">CDVInvokedUrlCommand.h</a>, <a href="https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/Public/CDVPluginResult.h">CDVPluginResult.h</a>, -and <a href="https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/Public/CDVCommandDelegate.h">CDVCommandDelegate.h</a>.</p> + <p>The header file defines the methods and properties that are exposed to other native classes. We also expose the methods that the front-end WebView requests so that Cordova can locate and invoke them.</p> -<h2>iOS CDVPluginResult Message Types</h2> + <p>In this example, we are exposing the <code>echo</code> method:</p> -<p>You can use <code>CDVPluginResult</code> to return a variety of result types back to -the JavaScript callbacks, using class methods that follow this pattern:</p> + <div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="cp">#import <Cordova/Cordova.h> +</span> + <span class="k">@interface</span> <span class="nc">Echo</span> <span class="p">:</span> <span class="nc">CDVPlugin</span> -<div class="language-objective_c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">+</span> <span class="p">(</span><span class="n">CDVPluginResult</span><span class="o">*</span><span class="p">)</span><span class="nf">resultWithStatus</span><span class="p">:(</span><span class="n">CDVCommandStatus</span><span class="p">)</span><span class="nv">statusOrdinal</span> <span class="n">messageAs</span><span class="p">...</span> -</code></pre></div></div> + <span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">echo</span><span class="p">:(</span><span class="n">CDVInvokedUrlCommand</span><span class="o">*</span><span class="p">)</span><span class="nv">command</span><span class="p">;</span> -<p>You can create <code>String</code>, <code>Int</code>, <code>Double</code>, <code>Bool</code>, <code>Array</code>, -<code>Dictionary</code>, <code>ArrayBuffer</code>, and <code>Multipart</code> types. You can also leave -out any arguments to send a status, or return an error, or even choose -not to send any plugin result, in which case neither callback fires.</p> + <span class="k">@end</span> +</code></pre></div> </div> -<p>Note the following for complex return values:</p> + <p><strong>Echo.m (Source File):</strong></p> -<ul> - <li> - <p><code>messageAsArrayBuffer</code> expects <code>NSData*</code> and converts to an -<code>ArrayBuffer</code> in the JavaScript callback. Likewise, any -<code>ArrayBuffer</code> the JavaScript sends to a plugin are converted to -<code>NSData*</code>.</p> + <p>The implementation source code (.m files) is where the business logic is performed.</p> + + <p>In this example, when the <code>echo</code> method is invoked, it examines the contents of the first argument to determine if there is something to echo back to the front-end WebView. If there is content, a <code>OK</code> result is returned with the message; otherwise, an <code>ERROR</code> is returned.</p> + + <div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="cp">#import "Echo.h" +</span> <span class="cp">#import <Cordova/Cordova.h> +</span> + <span class="k">@implementation</span> <span class="nc">Echo</span> + + <span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">echo</span><span class="p">:(</span><span class="n">CDVInvokedUrlCommand</span><span class="o">*</span><span class="p">)</span><span class="nv">command</span> + <span class="p">{</span> + <span class="n">CDVPluginResult</span><span class="o">*</span> <span class="n">pluginResult</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span> + <span class="n">NSString</span><span class="o">*</span> <span class="n">echo</span> <span class="o">=</span> <span class="p">[</span><span class="n">command</span><span class="p">.</span><span class="n">arguments</span> <span class="nf">objectAtIndex</span><span class="p">:</span><span class="mi">0</span><span class="p">];</span> + + <span class="k">if</span> <span class="p">(</span><span class="n">echo</span> <span class="o">!=</span> <span class="nb">nil</span> <span class="o">&&</span> <span class="p">[</span><span class="n">echo</span> <span class="nf">length</span><span class="p">]</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> + <span class="n">pluginResult</span> <span class="o">=</span> <span class="p">[</span><span class="n">CDVPluginResult</span> <span class="nf">resultWithStatus</span><span class="p">:</span><span class="n">CDVCommandStatus_OK</span> <span class="nf">messageAsString</span><span class="p">:</span><span class="n">echo</span><span class="p">];</span> + <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> + <span class="n">pluginResult</span> <span class="o">=</span> <span class="p">[</span><span class="n">CDVPluginResult</span> <span class="nf">resultWithStatus</span><span class="p">:</span><span class="n">CDVCommandStatus_ERROR</span><span class="p">];</span> + <span class="p">}</span> + + <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">commandDelegate</span> <span class="nf">sendPluginResult</span><span class="p">:</span><span class="n">pluginResult</span> <span class="nf">callbackId</span><span class="p">:</span><span class="n">command</span><span class="p">.</span><span class="n">callbackId</span><span class="p">];</span> + <span class="p">}</span> + + <span class="k">@end</span> +</code></pre></div> </div> </li> +</ul> + +<p><strong>Additional Notes:</strong></p> + +<ul> + <li>Plugin entry classes must extend <code>CDVPlugin</code>.</li> + <li>Supporting classes does not extend <code>CDVPlugin</code>.</li> <li> - <p><code>messageAsMultipart</code> expects an <code>NSArray*</code> containing any of the -other supported types, and sends the entire array as the <code>arguments</code> -to your JavaScript callback. This way, all of the arguments are -serialized or deserialized as necessary, so it is safe to return -<code>NSData*</code> as multipart, but not as <code>Array</code>/<code>Dictionary</code>.</p> + <p>The following <code>import</code> statements are required to be added to the top of the plugin entry classes.</p> + + <p>Swift based project will added the following to the source file:</p> + + <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="cp">#if canImport(Cordova)</span> + <span class="kd">import</span> <span class="kt">Cordova</span> + <span class="cp">#endif</span> +</code></pre></div> </div> + + <p>Objective-C based projects will added the following to the header & source files:</p> + + <div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="cp">#import <Cordova/Cordova.h> +</span></code></pre></div> </div> </li> </ul> -<h2>Echo iOS Plugin Example</h2> +<p><strong>Additional References:</strong></p> + +<p>For more details, see the following class headers:</p> + +<ul> + <li><a href="https://apache.github.io/cordova-ios/documentation/cordova/cdvinvokedurlcommand">CDVInvokedUrlCommand</a></li> + <li><a href="https://apache.github.io/cordova-ios/documentation/cordova/cdvpluginresult">CDVPluginResult</a></li> + <li><a href="https://apache.github.io/cordova-ios/documentation/cordova/cdvcommanddelegate">CDVCommandDelegate</a></li> +</ul> + +<h3>Configuring the <code>plugin.xml</code></h3> + +<h4>Adding Plugin Code to iOS Project</h4> + +<p>Now that we have our native source code written in our plugin project, we need to add these resource files to the application's directory. This ensures that the source code is available and used by the app. This can be achieved by defining the <code><source-file></code> and <code><header-file></code> elements in the <code>plugin.xml</code>.</p> + +<p>Below is an example of what this should look like inside the <code>plugin.xml</code>:</p> + +<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><platform</span> <span class="na">name=</span><span class="s">"ios"</span><span class="nt">></span> + <span class="c"><!-- If your plugin uses Swift --></span> + <span class="nt"><source-file</span> <span class="na">src=</span><span class="s">"src/ios/Echo.swift"</span> <span class="nt">/></span> + + <span class="c"><!-- If your plugin uses Objective-C --></span> + <span class="c"><!-- <header-file src="src/ios/Echo.h" /> --></span> + <span class="c"><!-- <source-file src="src/ios/Echo.m" /> --></span> +<span class="nt"></platform></span> +</code></pre></div></div> + +<p><em>Note:</em> If you are following along with <strong>Objective-C</strong>, be sure to update the above accordingly. In the example above, we are using <strong>Swift</strong>.</p> -<p>To match the JavaScript interface's <em>echo</em> feature described in -Application Plugins, use the <code>plugin.xml</code> to inject a <code>feature</code> -specification to the local platform's <code>config.xml</code> file:</p> +<p>What the above configuration does is for the iOS platform, it places the header file and source file in the appropriate location within the application. It also creates the necessary references in the Xcode project so that the application will recognize and use these files.</p> + +<h4>Setting Class Mapping for WebView-to-Native Communication</h4> + +<p>To be able to trigger native functionality in JavaScript, the native classes needs to be mapped within <code>plugin.xml</code> by using the <code><feature></code> element.</p> + +<p>Below is an example of what this should look like once the feature is added to the <code>plugin.xml</code> from the previous steps, combined:</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><platform</span> <span class="na">name=</span><span class="s">"ios"</span><span class="nt">></span> <span class="nt"><config-file</span> <span class="na">target=</span><span class="s">"config.xml"</span> <span class="na">parent=</span><span class="s">"/*"</span><span class="nt">></span> @@ -2503,81 +2576,135 @@ specification to the local platform's <code>config.xml</code> file:</p> <span class="nt"><param</span> <span class="na">name=</span><span class="s">"ios-package"</span> <span class="na">value=</span><span class="s">"Echo"</span> <span class="nt">/></span> <span class="nt"></feature></span> <span class="nt"></config-file></span> + + <span class="c"><!-- If your plugin uses Swift --></span> + <span class="nt"><source-file</span> <span class="na">src=</span><span class="s">"src/ios/Echo.swift"</span> <span class="nt">/></span> + + <span class="c"><!-- If your plugin uses Objective-C --></span> + <span class="c"><!-- <header-file src="src/ios/Echo.h" /> --></span> + <span class="c"><!-- <source-file src="src/ios/Echo.m" /> --></span> <span class="nt"></platform></span> </code></pre></div></div> -<p>Then we would add the following <code>Echo.h</code> and <code>Echo.m</code> files to the -<code>Plugins</code> folder within the Cordova-iOS application directory:</p> +<p>Specify the plugin's <code><feature></code> tag ensures that the necessary configuration is automatically injected into the Cordova-iOS project, as described in the <a href="../../hybrid/plugins/index.html">Plugin Development Guide</a>.</p> -<div class="language-objective_c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/********* Echo.h Cordova Plugin Header *******/</span> +<p>Lets break down what each element and attribute means.</p> -<span class="cp">#import <Cordova/CDVPlugin.h> -</span> -<span class="k">@interface</span> <span class="nc">Echo</span> <span class="p">:</span> <span class="nc">CDVPlugin</span> +<ul> + <li><code><feature></code> + <ul> + <li>The <code>name</code> attribute should match with the <code>service</code> parameter' value that is used in the JavaScript <code>cordova.exec</code> method call.</li> + </ul> + </li> + <li><code><param></code> + <ul> + <li>The <code>value</code> attribute should match the name of the plugin'sObjective-C or Swift class name.</li> + <li>The <code>name</code> attribute should always have the value of <code>ios-package</code> for iOS plugins.</li> + </ul> + </li> +</ul> -<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">echo</span><span class="p">:(</span><span class="n">CDVInvokedUrlCommand</span><span class="o">*</span><span class="p">)</span><span class="nv">command</span><span class="p">;</span> +<p>If the follow guidelines are not met, the plugin may compile but Cordova will not be able to access it.</p> -<span class="k">@end</span> +<p><strong>IMPORTANT NOTE:</strong> During the platform preparation for building the app, an auto-generated merged <code>config.xml</code> file is created. This file contains all platform-specific application configurations and plugin data gathered from the application's <code>config.xml</code> and the plugin's <code>plugin.xml</code>. The <code>config-file</code> block, as shown in the example above, ensures that the plugin's feature is injected into the merged <code>config. [...] -<span class="cm">/********* Echo.m Cordova Plugin Implementation *******/</span> +<h4>Configuring Plugin Initialization Timing</h4> -<span class="cp">#import "Echo.h" -#import <Cordova/CDVPlugin.h> -</span> -<span class="k">@implementation</span> <span class="nc">Echo</span> +<p>A single instance of a plugin object is typically created for the lifecycle of each <code>WKWebView</code>, though the instantiation timing depends on the plugin's implementation.</p> -<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">echo</span><span class="p">:(</span><span class="n">CDVInvokedUrlCommand</span><span class="o">*</span><span class="p">)</span><span class="nv">command</span> -<span class="p">{</span> - <span class="n">CDVPluginResult</span><span class="o">*</span> <span class="n">pluginResult</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span> - <span class="n">NSString</span><span class="o">*</span> <span class="n">echo</span> <span class="o">=</span> <span class="p">[</span><span class="n">command</span><span class="p">.</span><span class="n">arguments</span> <span class="nf">objectAtIndex</span><span class="p">:</span><span class="mi">0</span><span class="p">];</span> +<p>By default, plugins are instantiated when they are first referenced by a call from JavaScript. However, plugins can be configured to instantiate when the app loads by defining the <code>onload</code> attribute within a <code><param></code> element in the plugin's <code>plugin.xml</code> configuration file. This <code><param></code> should be added to the plugin's <code><feature></code> element.</p> - <span class="k">if</span> <span class="p">(</span><span class="n">echo</span> <span class="o">!=</span> <span class="nb">nil</span> <span class="o">&&</span> <span class="p">[</span><span class="n">echo</span> <span class="nf">length</span><span class="p">]</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> - <span class="n">pluginResult</span> <span class="o">=</span> <span class="p">[</span><span class="n">CDVPluginResult</span> <span class="nf">resultWithStatus</span><span class="p">:</span><span class="n">CDVCommandStatus_OK</span> <span class="nf">messageAsString</span><span class="p">:</span><span class="n">echo</span><span class="p">];</span> - <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> - <span class="n">pluginResult</span> <span class="o">=</span> <span class="p">[</span><span class="n">CDVPluginResult</span> <span class="nf">resultWithStatus</span><span class="p">:</span><span class="n">CDVCommandStatus_ERROR</span><span class="p">];</span> - <span class="p">}</span> +<p>For example:</p> - <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">commandDelegate</span> <span class="nf">sendPluginResult</span><span class="p">:</span><span class="n">pluginResult</span> <span class="nf">callbackId</span><span class="p">:</span><span class="n">command</span><span class="p">.</span><span class="n">callbackId</span><span class="p">];</span> -<span class="p">}</span> +<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><feature</span> <span class="na">name=</span><span class="s">"Echo"</span><span class="nt">></span> + <span class="nt"><param</span> <span class="na">name=</span><span class="s">"ios-package"</span> <span class="na">value=</span><span class="s">"Echo"</span> <span class="nt">/></span> + <span class="nt"><param</span> <span class="na">name=</span><span class="s">"onload"</span> <span class="na">value=</span><span class="s">"true"</span> <span class="nt">/></span> <span class="c"><!-- Initialize plugin on app load --></span> +<span class="nt"></feature></span> +</code></pre></div></div> + +<h3>Supporting Swift Package Manager (SPM)</h3> + +<p>Starting from Cordova-iOS 8 and greater, support for the Swift Package Manager (SPM) has been implemented. To start using SPM with your plugin, a <code>Package.swift</code> file will need to be created in the plugin's root directory and add the <code>package="swift"</code> attribute to the iOS <code><platform></code> element in your <code>plugin.xml</code> file.</p> + +<h4>Creating SPM's <code>Package.swift</code> File</h4> + +<p>In the plugin's root directory, create a new file called <code>Package.swift</code> with the following content:</p> + +<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// swift-tools-version:5.5</span> + +<span class="kd">import</span> <span class="kt">PackageDescription</span> + +<span class="k">let</span> <span class="nv">package</span> <span class="o">=</span> <span class="kt">Package</span><span class="p">(</span> + <span class="nv">name</span><span class="p">:</span> <span class="s">"cordova-plugin-echo"</span><span class="p">,</span> + <span class="nv">platforms</span><span class="p">:</span> <span class="p">[</span><span class="o">.</span><span class="nf">iOS</span><span class="p">(</span><span class="o">.</span><span class="n">v13</span><span class="p">)],</span> + <span class="nv">products</span><span class="p">:</span> <span class="p">[</span> + <span class="o">.</span><span class="nf">library</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"cordova-plugin-echo"</span><span class="p">,</span> <span class="nv">targets</span><span class="p">:</span> <span class="p">[</span><span class="s">"cordova-plugin-echo"</span><span class="p">])</span> + <span class="p">],</span> + <span class="nv">dependencies</span><span class="p">:</span> <span class="p">[</span> + <span class="c1">// This must be included as a dependency, with this format for it to work.</span> + <span class="o">.</span><span class="nf">package</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="s">"https://github.com/apache/cordova-ios.git"</span><span class="p">,</span> <span class="nv">branch</span><span class="p">:</span> <span class="s">"master"</span><span class="p">)</span> + <span class="p">],</span> + <span class="nv">targets</span><span class="p">:</span> <span class="p">[</span> + <span class="o">.</span><span class="nf">target</span><span class="p">(</span> + <span class="nv">name</span><span class="p">:</span> <span class="s">"cordova-plugin-echo"</span><span class="p">,</span> + <span class="nv">dependencies</span><span class="p">:</span> <span class="p">[</span> + <span class="o">.</span><span class="nf">product</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"Cordova"</span><span class="p">,</span> <span class="nv">package</span><span class="p">:</span> <span class="s">"cordova-ios"</span><span class="p">)</span> + <span class="p">],</span> + <span class="nv">path</span><span class="p">:</span> <span class="s">"src/ios"</span><span class="p">,</span> + <span class="nv">resources</span><span class="p">:</span> <span class="p">[],</span> + <span class="nv">publicHeadersPath</span><span class="p">:</span> <span class="s">"."</span> + <span class="p">)</span> + <span class="p">]</span> +<span class="p">)</span> +</code></pre></div></div> -<span class="k">@end</span> +<p>If the plugin is required to provide a privacy manifest file, the following line should be added to the <code>resources</code> element of the <code>cordova-plugin-echo</code> target: <code>.copy("Resources/PrivacyInfo.xcprivacy")</code>. +On top of the SPM declaration, be sure to also refer to the section titled <a href="#adding-a-privacy-manifest-file">Adding a Privacy Manifest File</a> to ensure that the actual resource file is properly declared in the <code>plugin.xml</code> so it is correctly injected into the app.</p> + +<p>If the plugin requires for any third-party dependencies, it should be added to the <code>dependencies</code> element, and the <code>target</code>'s <code>dependencies</code>.</p> + +<p>For example:</p> + +<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">dependencies</span><span class="p">:</span> <span class="p">[</span> + <span class="o">...</span> + <span class="o">.</span><span class="nf">package</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"SomePackageName"</span><span class="p">,</span> <span class="nv">url</span><span class="p">:</span> <span class="s">"..."</span><span class="p">,</span> <span class="nv">from</span><span class="p">:</span> <span class="s">"1.0.0"</span><span class="p">),</span> +<span class="p">],</span> +<span class="nv">targets</span><span class="p">:</span> <span class="p">[</span> + <span class="o">.</span><span class="nf">target</span><span class="p">(</span> + <span class="o">...</span> + <span class="nv">dependencies</span><span class="p">:</span> <span class="p">[</span> + <span class="o">.</span><span class="nf">product</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"Cordova"</span><span class="p">,</span> <span class="nv">package</span><span class="p">:</span> <span class="s">"cordova-ios"</span><span class="p">),</span> + <span class="o">.</span><span class="nf">product</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"SomePackageLibraryName"</span><span class="p">,</span> <span class="nv">package</span><span class="p">:</span> <span class="s">"SomePackageName"</span><span class="p">)</span> + <span class="p">],</span> + <span class="p">)</span> +<span class="p">]</span> </code></pre></div></div> -<p>The necessary imports at the top of the file extends the class from -<code>CDVPlugin</code>. In this case, the plugin only supports a single <code>echo</code> -action. It obtains the echo string by calling the <code>objectAtIndex</code> -method get the first parameter of the <code>arguments</code> array, which -corresponds to the arguments passed in by the JavaScript <code>exec()</code> -function.</p> +<h3>Additional Native Side Implementation</h3> + +<h4>Executing Plugin Initialization Logic</h4> -<p>It checks the parameter to make sure it is not <code>nil</code> or an empty -string, returning a <code>PluginResult</code> with an <code>ERROR</code> status if so. If -the parameter passes the check, it returns a <code>PluginResult</code> with an -<code>OK</code> status, passing in the original <code>echo</code> string. Finally, it sends -the result to <code>self.commandDelegate</code>, which executes the <code>exec</code> -method's success or failure callbacks on the JavaScript side. If the -success callback is called, it passes in the <code>echo</code> parameter.</p> +<p>If the plugin has any logic that should execute on the during the plugin's initialization process, the <code>pluginInitialize</code> method should be defined in the plugin's class.</p> -<h2>iOS Integration</h2> +<p>For example, if the plugin has defined <code>onload</code> as <code>true</code>, when the app loads, the <code>pluginInitialize</code> method will be executed. Because this is triggered during app load, there is no <code>callbackID</code> so the <code>pluginInitialize</code> method can not return any results to the WebView. If results matter, they would need to be stored in some manar and later fetched with a JavaScript API call.</p> -<p>The <code>CDVPlugin</code> class features other methods that your plugin can -override. For example, you can capture the <a href="../../../cordova/events/events.html#pause">pause</a>, <a href="../../../cordova/events/events.html#resume">resume</a>, app -terminate and <code>handleOpenURL</code> events. See the -<a href="https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/Public/CDVPlugin.h">CDVPlugin.h</a> and <a href="https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/Public/CDVPlugin.m">CDVPlugin.m</a> -classes for guidance.</p> +<h4>Handeling Long-running & Background Activities</h4> -<h3>WKURLSchemeTask Hook</h3> +<p>Plugins with long-running requests or background activities, such as media playback, listeners, or those that maintain internal state, should implement the <code>onReset</code> method to cancel these requests or clean up after those activities.</p> -<p>The <a href="https://developer.apple.com/documentation/webkit/wkurlschemetask">WKURLSchemeTask</a> is an interface Cordova's main WKWebView uses to load files from your app's bundle. You can create your own custom schemes or custom loading code for the webview by implementing the <code>- (BOOL) overrideSchemeTask: (id <WKURLSchemeTask>)urlSchemeTask</code> method in a plugin.</p> +<p>The <code>onReset</code> method is called when the <code>WKWebView</code> navigates to a new page or refreshes, triggering a reload of the JavaScript.</p> -<h2>Threading</h2> +<h4>Hooking into WKURLSchemeTask</h4> -<p>Plugin methods ordinarily execute in the same thread as the main -interface. If your plugin requires a great deal of processing or -requires a blocking call, you should use a background thread. For -example:</p> +<p>The <a href="https://developer.apple.com/documentation/webkit/wkurlschemetask">WKURLSchemeTask</a> is an interface Cordova's main WKWebView uses to load files from your app's bundle. You can create your own custom schemes or custom loading code for the WebView by implementing the <code>- (BOOL) overrideSchemeTask: (id <WKURLSchemeTask>)urlSchemeTask</code> method in a plugin.</p> -<div class="language-objective_c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">myPluginMethod</span><span class="p">:(</span><span class="n">CDVInvokedUrlCommand</span><span class="o">*</span><span class="p">)</span><span class="nv">command</span> +<h4>Using Background Threads</h4> + +<p>Plugin methods ordinarily execute in the same thread as the main interface. If your plugin requires a great deal of processing or requires a blocking call, you should use a background thread. It is important to note that any operations involving the UI, such as displaying alerts, changing colors, or performing other visual updates, must be executed on the main thread.</p> + +<p>For example:</p> + +<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">myPluginMethod</span><span class="p">:(</span><span class="n">CDVInvokedUrlCommand</span><span class="o">*</span><span class="p">)</span><span class="nv">command</span> <span class="p">{</span> <span class="c1">// Check command.arguments here.</span> <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">commandDelegate</span> <span class="nf">runInBackground</span><span class="p">:</span><span class="o">^</span><span class="p">{</span> @@ -2590,22 +2717,134 @@ example:</p> <span class="p">}</span> </code></pre></div></div> -<h2>Debugging iOS Plugins</h2> +<h4>Adding a Privacy Manifest File</h4> + +<p>As of May 1, 2024, Apple requires a privacy manifest file to be created for apps and third-party SDKs. The purpose of the privacy manifest file is to explain the data being collected and the reasons for the required APIs it uses.</p> + +<p>Plugins can include a pre-bundled <code>PrivacyInfo.xcprivacy</code> file that lists any privacy-sensitive APIs they use, along with the reasons for their usage.</p> + +<p>It is recommended to review the following Apple Developer document, "<a href="https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests">Describing data use in privacy manifests</a>", to understand the list of known <code>NSPrivacyCollectedDataTypes</code> and <code>NSPrivacyCollectedDataTypePurposes</code>.</p> + +<p>Ensure all four keysβ<code>NSPrivacyTracking</code>, <code>NSPrivacyTrackingDomains</code>, <code>NSPrivacyAccessedAPITypes</code>, and <code>NSPrivacyCollectedDataTypes</code>βare defined, even if you are not making an addition to the other items. Apple requires all to be defined.</p> + +<p>Once you've identified what the contents of the <code>PrivacyInfo.xcprivacy</code> will look like, lets start creating the bundle and loading it as a resource.</p> + +<ol> + <li> + <p>Create a directory named <code>CDVEcho.bundle</code> inside the <code>src/ios</code> directory. Make sure the bundle name is unique enough to avoid conflicts with other plugins.</p> + </li> + <li> + <p>Inside the new <code>CDVEcho.bundle</code> directory, create a privacy manifest file named <code>PrivacyInfo.xcprivacy</code>.</p> + </li> + <li> + <p>Add the contents you've identified for this file. Here's an example:</p> + + <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"><!-- Example PrivacyInfo.xcprivacy Contents --></span> +<span class="cp"><?xml version="1.0" encoding="UTF-8"?></span> +<span class="cp"><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"></span> +<span class="nt"><plist</span> <span class="na">version=</span><span class="s">"1.0"</span><span class="nt">></span> +<span class="nt"><dict></span> + <span class="nt"><key></span>NSPrivacyTracking<span class="nt"></key></span> + <span class="nt"><false/></span> + <span class="nt"><key></span>NSPrivacyTrackingDomains<span class="nt"></key></span> + <span class="nt"><array/></span> + <span class="nt"><key></span>NSPrivacyAccessedAPITypes<span class="nt"></key></span> + <span class="nt"><array/></span> + <span class="nt"><key></span>NSPrivacyCollectedDataTypes<span class="nt"></key></span> + <span class="nt"><array/></span> +<span class="nt"></dict></span> +<span class="nt"></plist></span> +</code></pre></div> </div> + </li> + <li> + <p>Update your <code>plugin.xml</code> to load the <code>CDVEcho.bundle</code> into the app's resources.</p> + + <p>Inside the iOS <code><platform></code> element, add a <code><resource-file></code> element pointing to the <code>CDVEcho.bundle</code> directory:</p> + + <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><platform</span> <span class="na">name=</span><span class="s">"ios"</span><span class="nt">></span> + <span class="nt"><resource-file</span> <span class="na">src=</span><span class="s">"src/ios/CDVEcho.bundle"</span> <span class="na">target=</span><span class="s">"CDVEcho.bundle"</span> <span class="nt">/></span> +<span class="nt"></platform></span> +</code></pre></div> </div> + </li> + <li> + <p><strong>Optional:</strong> If your plugin supports Swift Package Manager, refer to the section <a href="#creating-spms-packageswift-file">Creating SPM's <code>Package.swift</code> File</a> to ensure the privacy manifest is also included as a resource file.</p> + </li> +</ol> + +<h2>CDVPluginResult Message Types</h2> + +<p>You can use <a href="https://apache.github.io/cordova-ios/documentation/cordova/cdvpluginresult"><code>CDVPluginResult</code></a> to return a variety of result types back to the JavaScript callbacks, using class methods that follow this pattern:</p> + +<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">+</span> <span class="p">(</span><span class="n">CDVPluginResult</span><span class="o">*</span><span class="p">)</span><span class="nf">resultWithStatus</span><span class="p">:(</span><span class="n">CDVCommandStatus</span><span class="p">)</span><span class="nv">statusOrdinal</span> <span class="n">messageAs</span><span class="p">...</span> +</code></pre></div></div> + +<p>The following types can be used:</p> + +<ul> + <li><code>String</code></li> + <li><code>Int</code></li> + <li><code>Double</code></li> + <li><code>Bool</code></li> + <li><code>Array</code></li> + <li><code>Dictionary</code></li> + <li><code>ArrayBuffer</code></li> + <li><code>Multipart</code></li> +</ul> + +<p>You can also leave out any arguments to send a status, or return an error, or even choose not to send any plugin result, in which case neither callback fires.</p> + +<p>Note the following for complex return values:</p> + +<ul> + <li> + <p><code>messageAsArrayBuffer</code> expects <code>NSData*</code> and will convert it to an <code>ArrayBuffer</code> in the JavaScript callback. Likewise, any <code>ArrayBuffer</code> the JavaScript sends to a native side will be converted to <code>NSData*</code>.</p> + </li> + <li> + <p><code>messageAsMultipart</code> expects an <code>NSArray*</code> containing any of the other supported types, and sends the entire array as the <code>arguments</code> to your JavaScript callback. This way, all of the arguments are serialized or deserialized as necessary, so it is safe to return <code>NSData*</code> as multipart, but not as <code>Array</code>/<code>Dictionary</code>.</p> + </li> +</ul> + +<h2>Other Supported <code>CDVPlugin</code> Features</h2> + +<p>The <code>CDVPlugin</code> class features other methods that a plugin can override.</p> + +<p>For example, the plugin can capture:</p> +<ul> + <li><a href="../../../cordova/events/events.html#pause"><code>pause</code></a> Event</li> + <li><a href="../../../cordova/events/events.html#resume"><code>resume</code></a> Event</li> + <li>App Terminate Event</li> + <li><code>handleOpenURL</code> events</li> +</ul> + +<p>For additional reference, see the following class documentation:</p> + +<ul> + <li><a href="https://apache.github.io/cordova-ios/documentation/cordova/cdvplugin">CDVPlugin</a></li> +</ul> + +<h2>Debugging Plugins for iOS</h2> + +<p>To debug the native side, you will need to use Xcode's built-in debugger.</p> + +<p>For JavaScript, you can launch the Safari Web Inspector and attach it to the running application process. The app can be running on either an iOS Simulator or device.</p> + +<p>Generally, its recommended to use a debug build for testing as it should already allow the WebView to be inspectable. If for any reason you need to test on a release build, you can enable WebView Inspector by setting the <code>InspectableWebview</code> config preference to <code>true</code> in the application's <code>config.xml</code>.</p> + +<p>E.g.</p> + +<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><preference</span> <span class="na">name=</span><span class="s">"InspectableWebview"</span> <span class="na">value=</span><span class="s">"true"</span> <span class="nt">/></span> +</code></pre></div></div> -<p>To debug on the Objective-C side, you need Xcode's built-in debugger. -For JavaScript, you can attach Safari to the app running within the iOS Simulator/Device.</p> +<p>For security purpose, its highly unrecommended to enable the <code>InspectableWebview</code> for release builds. If you do set it, remove the setting before deploy the app to the app store.</p> <h2>Common Pitfalls</h2> <ul> <li> - <p>Don't forget to add your plugin's mapping to <code>config.xml</code>. If you -forget, an error is logged in the Xcode console.</p> + <p>Don't forget to add your plugin's mapping to <code>plugin.xml</code>. If you forget, an error is logged in the Xcode console.</p> </li> <li> - <p>Don't forget to add any hosts you connect to in the allow list, as -described in Domain <a href="../../appdev/allowlist/index.html">Allow List Guide</a>. If you forget, an error is -logged in the Xcode console.</p> + <p>Don't forget to add any hosts you connect to in the allow list, as described in Domain <a href="../../appdev/allowlist/index.html">Allow List Guide</a>. If you forget, an error is logged in the Xcode console.</p> </li> </ul> diff --git a/feed.xml b/feed.xml index 7418a0661e..288a35aac7 100644 --- a/feed.xml +++ b/feed.xml @@ -6,8 +6,8 @@ </description> <link>https://cordova.apache.org/</link> <atom:link href="https://cordova.apache.org/feed.xml" rel="self" type="application/rss+xml"/> - <pubDate>Fri, 04 Jul 2025 07:28:36 +0000</pubDate> - <lastBuildDate>Fri, 04 Jul 2025 07:28:36 +0000</lastBuildDate> + <pubDate>Mon, 07 Jul 2025 13:11:48 +0000</pubDate> + <lastBuildDate>Mon, 07 Jul 2025 13:11:48 +0000</lastBuildDate> <generator>Jekyll v4.4.1</generator> <item> --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cordova.apache.org For additional commands, e-mail: commits-h...@cordova.apache.org