Skip to content

Commit 4ceb5bb

Browse files
committed
feat: create expo plugin
1 parent df2b44b commit 4ceb5bb

7 files changed

Lines changed: 307 additions & 15 deletions

File tree

.github/ISSUE_TEMPLATE/bug_report.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ body:
3636
attributes:
3737
label: Library version
3838
description: What version of the library are you using?
39-
placeholder: "x.x.x"
39+
placeholder: 'x.x.x'
4040
validations:
4141
required: true
4242
- type: textarea
@@ -61,7 +61,7 @@ body:
6161
- type: input
6262
id: reproducible-example
6363
attributes:
64-
label: Reproducible example repository
64+
label: Reproducible example repository
6565
description: Please provide a link to a repository on GitHub with a reproducible example.
6666
validations:
6767
required: true

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ jobs:
111111
112112
- name: Build example for Android
113113
env:
114-
JAVA_OPTS: "-XX:MaxHeapSize=6g"
114+
JAVA_OPTS: '-XX:MaxHeapSize=6g'
115115
run: |
116116
yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}"
117117

.yarnrc.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ nmHoistingLimits: workspaces
33

44
plugins:
55
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
6-
spec: "@yarnpkg/plugin-interactive-tools"
6+
spec: '@yarnpkg/plugin-interactive-tools'
77
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
8-
spec: "@yarnpkg/plugin-workspace-tools"
8+
spec: '@yarnpkg/plugin-workspace-tools'
99

1010
yarnPath: .yarn/releases/yarn-3.6.1.cjs

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ A powerful, easy-to-use React Native library for real-time speech-to-text conver
1919

2020
## 📱 Demo
2121

22-
| iOS | Android |
23-
| --- | ------- |
22+
| iOS | Android |
23+
| --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
2424
| <video src="https://github.com/user-attachments/assets/e48938ca-87a5-4343-ab76-cecc91b8380f" width="300"> | <video src="https://github.com/user-attachments/assets/2fbf41db-bf2d-4759-9181-0b8376a96dc1" width="300"> |
2525

2626
## 📦 Installation
@@ -163,9 +163,11 @@ await start({ language: 'en-US' });
163163
```
164164

165165
**Options:**
166+
166167
- `language` (string, required): Language code (e.g., "en-US", "fr-FR", "es-ES", "de-DE")
167168

168169
**Throws:**
170+
169171
- `PERMISSION_DENIED`: User denied permissions
170172
- `NOT_AVAILABLE`: Speech recognition not available
171173
- `START_FAILED`: Failed to start recognition
@@ -195,6 +197,7 @@ const granted = await requestPermissions({
195197
```
196198

197199
**Options (Android only):**
200+
198201
- `title` (string, optional): Dialog title
199202
- `message` (string, optional): Dialog message
200203
- `buttonNeutral` (string, optional): Neutral button text
@@ -238,6 +241,7 @@ listener.remove();
238241
```
239242

240243
**SpeechResult:**
244+
241245
- `transcript` (string): The recognized text
242246
- `confidence` (number): Confidence score from 0.0 to 1.0
243247
- `isFinal` (boolean): `true` for final result, `false` for partial
@@ -258,6 +262,7 @@ listener.remove();
258262
```
259263

260264
**SpeechError:**
265+
261266
- `code` (string): Error code (see [Error Codes](#error-codes))
262267
- `message` (string): Human-readable error message
263268

@@ -341,10 +346,12 @@ Availability depends on the device and platform. Use `isAvailable()` to check.
341346
### "Permission denied" error
342347

343348
**iOS:**
349+
344350
- Make sure you've added `NSSpeechRecognitionUsageDescription` and `NSMicrophoneUsageDescription` to your `Info.plist`
345351
- Check that the user granted permissions in Settings > Your App
346352

347353
**Android:**
354+
348355
- Ensure `RECORD_AUDIO` permission is in `AndroidManifest.xml`
349356
- Call `requestPermissions()` before `start()`
350357

@@ -385,6 +392,7 @@ Availability depends on the device and platform. Use `isAvailable()` to check.
385392
### Network errors
386393

387394
Some speech recognition services require internet connectivity:
395+
388396
- **iOS**: On-device recognition available on iOS 13+ for some languages
389397
- **Android**: Depends on the device's speech recognition provider
390398

app.plugin.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const { withInfoPlist, withAndroidManifest } = require('@expo/config-plugins');
2+
3+
const withSpeechToText = (config, props = {}) => {
4+
const {
5+
microphonePermission = 'Allow $(PRODUCT_NAME) to access your microphone to record audio for speech recognition',
6+
speechRecognitionPermission = 'Allow $(PRODUCT_NAME) to use speech recognition to convert your voice to text',
7+
} = props;
8+
9+
config = withInfoPlist(config, (config) => {
10+
config.modResults.NSSpeechRecognitionUsageDescription =
11+
speechRecognitionPermission;
12+
config.modResults.NSMicrophoneUsageDescription = microphonePermission;
13+
return config;
14+
});
15+
16+
config = withAndroidManifest(config, (config) => {
17+
const androidManifest = config.modResults.manifest;
18+
19+
if (!androidManifest['uses-permission']) {
20+
androidManifest['uses-permission'] = [];
21+
}
22+
23+
const hasRecordAudio = androidManifest['uses-permission'].some(
24+
(perm) => perm.$['android:name'] === 'android.permission.RECORD_AUDIO'
25+
);
26+
27+
if (!hasRecordAudio) {
28+
androidManifest['uses-permission'].push({
29+
$: { 'android:name': 'android.permission.RECORD_AUDIO' },
30+
});
31+
}
32+
33+
return config;
34+
});
35+
36+
return config;
37+
};
38+
39+
module.exports = withSpeechToText;

package.json

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@dbkable/react-native-speech-to-text",
33
"version": "0.1.0",
4-
"description": "Convert audio to text",
4+
"description": "Cross-platform speech recognition library for React Native using native APIs (Speech Framework & SpeechRecognizer)",
55
"main": "./lib/module/index.js",
66
"types": "./lib/typescript/src/index.d.ts",
77
"exports": {
@@ -18,6 +18,7 @@
1818
"android",
1919
"ios",
2020
"cpp",
21+
"app.plugin.js",
2122
"*.podspec",
2223
"react-native.config.js",
2324
"!ios/build",
@@ -43,7 +44,14 @@
4344
"keywords": [
4445
"react-native",
4546
"ios",
46-
"android"
47+
"android",
48+
"speech",
49+
"speech-to-text",
50+
"voice",
51+
"recognition",
52+
"stt",
53+
"transcription",
54+
"expo"
4755
],
4856
"repository": {
4957
"type": "git",
@@ -64,6 +72,7 @@
6472
"@eslint/eslintrc": "^3.3.1",
6573
"@eslint/js": "^9.35.0",
6674
"@evilmartians/lefthook": "^1.12.3",
75+
"@expo/config-plugins": "^54.0.2",
6776
"@react-native-community/cli": "20.0.1",
6877
"@react-native/babel-preset": "0.81.1",
6978
"@react-native/eslint-config": "^0.81.1",
@@ -160,5 +169,10 @@
160169
"languages": "kotlin-objc",
161170
"type": "turbo-module",
162171
"version": "0.54.8"
172+
},
173+
"expo": {
174+
"plugins": [
175+
"./app.plugin.js"
176+
]
163177
}
164178
}

0 commit comments

Comments
 (0)