How to Build a Custom Cordova Plugin for Native Text-to-Speech on Android and iOS

Creating a Custom Cordova Plugin: Native Text-to-Speech for Android & iOS

Need native-level control in your Cordova app? In this article, we'll build a fully custom Cordova plugin from scratch that connects JavaScript to the native Text-to-Speech (TTS) capabilities of Android and iOS.

🧱 Step 1: Plugin Folder Structure

In your workspace, create a plugin folder:

cordova-plugin-tts/
├── plugin.xml
├── www/
│   └── TTS.js
├── src/
│   ├── android/
│   │   └── TTSPlugin.java
│   └── ios/
│       └── TTSPlugin.swift

📄 Step 2: plugin.xml (metadata & platform bindings)

<plugin id="cordova-plugin-tts" version="1.0.0"
        xmlns="http://apache.org/cordova/ns/plugins/1.0"
        xmlns:android="http://schemas.android.com/apk/res/android">
  <name>TTSPlugin</name>

  <js-module src="www/TTS.js" name="TTS">
    <clobbers target="TTS" />
  </js-module>

  <platform name="android">
    <source-file src="src/android/TTSPlugin.java" target-dir="src/com/example/tts"/>
  </platform>

  <platform name="ios">
    <source-file src="src/ios/TTSPlugin.swift" />
  </platform>
</plugin>

💬 Step 3: JavaScript Bridge (www/TTS.js)

var exec = require('cordova/exec');

exports.speak = function(text, success, error) {
  exec(success, error, "TTSPlugin", "speak", [text]);
};

🤖 Step 4: Android Implementation (src/android/TTSPlugin.java)

package com.example.tts;

import android.speech.tts.TextToSpeech;
import android.content.Context;
import org.apache.cordova.*;
import org.json.JSONArray;

import java.util.Locale;

public class TTSPlugin extends CordovaPlugin {

  private TextToSpeech tts;

  @Override
  public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
    if ("speak".equals(action)) {
      final String text = args.optString(0);

      cordova.getActivity().runOnUiThread(() -> {
        tts = new TextToSpeech(cordova.getContext(), status -> {
          if (status == TextToSpeech.SUCCESS) {
            tts.setLanguage(Locale.US);
            tts.speak(text, TextToSpeech.QUEUE_FLUSH, null, null);
            callbackContext.success("Speaking");
          } else {
            callbackContext.error("TTS init failed");
          }
        });
      });
      return true;
    }
    return false;
  }
}

🍎 Step 5: iOS Implementation (src/ios/TTSPlugin.swift)

import Foundation
import AVFoundation
import Cordova

@objc(TTSPlugin) class TTSPlugin: CDVPlugin {
  var synthesizer = AVSpeechSynthesizer()

  @objc(speak:)
  func speak(command: CDVInvokedUrlCommand) {
    guard let text = command.arguments[0] as? String else {
      return
    }

    let utterance = AVSpeechUtterance(string: text)
    utterance.voice = AVSpeechSynthesisVoice(language: "en-US")

    synthesizer.speak(utterance)

    let pluginResult = CDVPluginResult(status: CDVCommandStatus_OK, messageAs: "Speaking")
    self.commandDelegate.send(pluginResult, callbackId: command.callbackId)
  }
}

📦 Step 6: Add Plugin to Your Cordova Project

In your Cordova project:

cordova plugin add ../cordova-plugin-tts

📱 Step 7: Use It in Your App

<button onclick="TTS.speak('Hello from native TTS!')">Speak</button>

✅ Final Notes

  • You now have a working, cross-platform native plugin.
  • For production, manage lifecycle (release TTS engine, handle errors, language options).
  • Plugins like this are ideal when you need performance or access to platform-specific features not exposed by default Cordova APIs.

🧠 Related Resources

With this approach, you’ve bridged JavaScript to native power — one of Cordova’s most underrated strengths.

Comments

Popular posts from this blog

Do You Really Need Advanced Algorithms to Be a Great Developer in 2025?

Advanced Chrome Dino Game (HTML + JS + Sprites)

Boost Productivity Using AI and REST APIs