In questa guida scopriremo, insieme ad una piccola implementazione di esempio, tutte le fasi della creazione di un plugin Capacitor 3, e come utilizzarlo successivamente all’interno di un progetto di Ionic 5 di esempio.
Premessa
Il plugin generato è compatibile con Android, iOS e anche browser web come WebApp. In questa guida però effettueremo solo l’implementazione di un metodo nativo Android scritto in Java, per testarlo successivamente all’interno di un progetto Ionic.
Nel nostro caso utilizziamo una macchina con sistema operativo Windows ma l’intera procedura può essere replicata anche su sistema operativo MacOS o su una distribuzione Linux. Tutti gli strumenti utilizzati sono infatti multipiattaforma.
Una piccola precisazione, come per la compilazione iOS è necessario MacOS, anche per la compilazione di un plugin Capacitor iOS è necessario XCode e quindi MacOS.
Requisiti
Sulla nostra macchina avremo bisogno di:
- Node LTS (14.17.4 al momento della scrittura di questo articolo);
- Visual Studio Code;
- Android Studio con gli strumenti SDK installati
Iniziamo
Creiamo una nuova cartella di lavoro e posizioniamoci al suo interno con un terminale CMD o PowerShell e lanciamo il comando di inizializzazione di un nuovo plugin:
npm init @capacitor/plugin
Durante l’esecuzione della procedura verranno richiesti alcuni parametri di configurazione:
- Il nome del pacchetto NPM che avrà il plugin, sarà quello utilizzato anche per un eventuale pubblicazione;
- La cartella che verrà utilizzata in fase di sviluppo, di default quella in cui abbiamo avviato il terminale;
- L’identificativo univoco del pacchetto, nel nostro esempio com.cloudsurfers.plugins.examplecapacitor;
- Il nome della classe principale, Example nel nostro caso;
- L’URL del repository, che può essere quella di GitHub del progetto;
- L’autore, anche opzionale;
- La licenza del plugin, per esempio licenza MIT;
- Infine, una breve descrizione del progetto.
Apriamo quindi la cartella principale del progetto appena creata con VSCODE e da terminale, sempre dalla cartella principale, lanciamo il comando:
npm install
per scaricare tutti i pacchetti necessari.
Implementazione del nuovo metodo
Il progetto generato in automatico con il comando di Init ci propone già un esempio di implementazione di un metodo chiamato echo, disponibile sia come implementazione Web, iOS ed Android. La sua definizione si trova all’interno del file TypeScript definition.ts, all’interno della cartella src.
Il file si presenta in questo modo:
export interface ExamplePlugin {
echo(options: { value: string }): Promise<{ value: string }>;
}
Andiamo ad aggiungere la definizione nel nostro nuovo metodo, che chiamiamo getCarInfo e di due nuove interfacce di supporto CarInfoOptions e CarModelInfo:
export interface ExamplePlugin {
echo(options: { value: string }): Promise<{ value: string }>;
getCarInfo(options: CarInfoOptions): Promise<CarModelInfo[]>;
}
export interface CarInfoOptions {
manufacturer: string
}
export interface CarModelInfo {
name: string,
engine: string
}
Il funzionamento sarà molto semplice, facendo una chiamata al metodo getCarInfo, passando come argomento delle opzioni contenenti ad esempio il nome di una casa automobilistica (“FIAT”), il metodo ritornerà l’elenco di modelli di auto di quella casa automobilistica con alcune informazioni come il nome del modello ed il motore.
Questo esempio, un po’ forzato, non necessita dell’accesso al codice nativo del sistema (Android o iOS) per portare a termine il risultato, ma ci aiuta a capire come funzionano e come viene sfruttato il linguaggio nativo all’interno dei plugin Capacitor e successivamente poter effettuare implementazioni più avanzate che devono necessariamente sfruttare componenti specifici del sistema su cui vengono eseguiti.
Detto questo, l’implementazione Web, quindi Browser Web, si trova all’interno del file TypeScript web.ts, sempre all’interno della cartella src.
import { WebPlugin } from '@capacitor/core';
import type {
ExamplePlugin,
CarInfoOptions,
CarModelInfo
} from './definitions';
export class ExampleWeb extends WebPlugin implements ExamplePlugin {
async echo(options: { value: string }): Promise<{ value: string }> {
console.log('ECHO', options);
return options;
}
async getCarInfo(options: CarInfoOptions): Promise<CarModelInfo[]> {
console.log(options);
throw Error("Implementazione Web non presente!");
}
}
Nel nostro caso, visto che andremo ad effettuare solo l’implementazione Android, facciamo in modo che, nell’implementazione Web sopra, ritorni un errore “Implementazione Web non presente!” quando il metodo viene invocato.
Implementazione Android
Utilizzando Android Studio, apriamo la cartella android presente all’interno del progetto del plugin.
Apriamo il file ExamplePlugin.java (nel nostro caso si chiama così per aver scelto Example come classe principale in fase di configurazione) ed aggiungiamo anche qui il metodo getCarInfo:
package com.cloudsurfers.plugins.examplecapacitor;
import com.getcapacitor.JSArray;
import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
@CapacitorPlugin(name = "Example")
public class ExamplePlugin extends Plugin {
private Example implementation = new Example();
@PluginMethod
public void echo(PluginCall call) {
String value = call.getString("value");
JSObject ret = new JSObject();
ret.put("value", implementation.echo(value));
call.resolve(ret);
}
@PluginMethod
public void getCarInfo(PluginCall call) {
String manufacturer = call.getString("manufacturer");
JSObject ret = new JSObject();
JSArray arr = new JSArray();
if(manufacturer.equals("FIAT")){
arr.put(new JSObject()
.put("name", "PUNTO")
.put("engine", "1.0 BENZINA"));
arr.put(new JSObject()
.put("name", "PUNTO")
.put("engine", "1.3 DIESEL"));
arr.put(new JSObject()
.put("name", "TIPO")
.put("engine", "1.3 DIESEL"));
arr.put(new JSObject()
.put("name", "CROMA")
.put("engine", "2.0 DIESEL"));
} else if (manufacturer.equals("SEAT")){
arr.put(new JSObject()
.put("name", "IBIZA")
.put("engine", "1.0 BENZINA"));
arr.put(new JSObject()
.put("name", "LEON")
.put("engine", "2.0 DIESEL"));
}
ret.put("models", arr);
call.resolve(ret);
}
}
Come si può vedere, utilizzando l’oggetto PluginCall riusciamo ad ottenere gli eventuali argomenti passati come parametro al metodo, ed eventualmente, una volta eseguita la logica, risolvere con un output o rigettare la chiamata con un errore, se serve.
L’output deve essere di tipo JSObject. In questo caso l’output sarà un JSON composto da un array “models” (JSArray) con a sua volta al suo interno uno o più JSObject con due proprietà: name ed engine.
A questo punto l’implementazione Android è terminata, si prosegue così con la compilazione e successivo test locale del plugin.
Compilazione
Dalla cartella principale del progetto del plugin, lanciamo da terminale:
npm run build
per compilare il plugin e permetterne l’utilizzo all’interno di un progetto Ionic.
Al termine della procedura il plugin è già pronto per essere testato ed eventualmente pubblicato su NPM.
Utilizzare il plugin in un progetto Ionic
Creiamo un nuovo progetto Ionic (Angular + Capacitor) con il comando:
ionic start
seguiamo la procedura guidata, nel mio caso ho scelto un template app tab.
Proseguiamo lanciando:
npm install
npm install @capacitor/android
per l’installazione di tutti i pacchetti necessari e successivamente:
npm cap init
npm cap add android
per inizializzare Capacitor e aggiungere la piattaforma Android al progetto.
A questo punto il nostro progetto Ionic di esempio è pronto ed è già compilabile ed eseguibile su un dispositivo Android, che sia un dispositivo fisico o un emulatore su Android Studio.
Tramite NPM installiamo ora il plugin creato in precedenza dalla sua cartella locale di sviluppo:
npm install "cartella_principale_locale_plugin"
Avendo scelto il template Angular Tab la prima pagina che viene caricata dall’app è la tab 1, quindi andiamo ad aprire il file tab1.page.ts ed importiamo il plugin e l’interfaccia per le opzioni come segue:
import { CarInfoOptions, Example } from 'cloudsurfers-example-capacitor-plugin';
All’interno dell’evento ngOnInit richiamiamo il nostro plugin:
ngOnInit(){
console.log(Example.getCarInfo({
manufacturer: "FIAT"
} as CarInfoOptions));
}
Ora compiliamo, sincronizziamo ed aggiorniamo i dati per il progetto Android. Lanciamo successivamente sull’emulatore o su un dispositivo fisico.
ionic build
npx cap update android
npx cap sync android
npx cap open android
All’avvio dell’app il plugin stamperà all’interno della console Javascript l’oggetto contenente tutti i modelli “FIAT” come impostato all’interno del parametro del nostro metodo getCarInfo:
Conclusione
Questo è un esempio abbastanza semplice di come creare un plugin Capacitor 3. Partendo da questo però è possibile sperimentare qualcosa di più avanzato ed interagire con componenti di sistema, per esempio interfacce Wifi o Camera e cucire un plugin ad-hoc per le nostre esigenze.
Qui trovare l’archivio compresso del progetto plugin di esempio creato sopra.