On this page
native bridge and plugins
introduction
the native bridge provides bidirectional communication between your javascript code and the native runtime. all device apis are exposed through a unified interface that works identically across ios, android, and desktop platforms.
the native bridge
bridge architecture
the bridge operates on a request response model. your javascript code sends method calls to the native layer, which executes the operation and returns a result asynchronously.
all communication uses json serialization. complex objects are automatically marshalled across the boundary. promises provide natural async handling on the javascript side.
calling native methods
invoke native functionality using the global bridge object:
1const result = await window.dstrnBridge.call('storage.get', { key: 'username' });2console.log(result.value);the call method accepts a method name and optional data payload. it returns a promise that resolves with the native response or rejects on error.
method names use dot notation to namespace functionality. all built in apis use the pattern category.action.
receiving native events
the native runtime can push events to your javascript code. listen for events using the bridge event emitter:
1window.dstrnBridge.on('app:background', () => {2 console.log('app moved to background');3});lifecycle events fire automatically when the app state changes. custom plugins can emit their own events using the same mechanism.
available apis
storage
persistent key value storage backed by native secure storage apis:
1await window.dstrnBridge.call('storage.set', { key: 'token', value: 'abc123' });2const result = await window.dstrnBridge.call('storage.get', { key: 'token' });3const keys = await window.dstrnBridge.call('storage.keys');4await window.dstrnBridge.call('storage.remove', { key: 'token' });5await window.dstrnBridge.call('storage.clear');
clipboard
read and write the system clipboard:
1await window.dstrnBridge.call('clipboard.write', { text: 'hello world' });2const result = await window.dstrnBridge.call('clipboard.read');3console.log(result.text);
haptics
trigger haptic feedback on supported devices:
1await window.dstrnBridge.call('haptic.impact', { style: 'medium' });2await window.dstrnBridge.call('haptic.notification', { type: 'success' });3await window.dstrnBridge.call('haptic.selection');
notifications
request permissions and display local notifications:
1const result = await window.dstrnBridge.call('notification.permission');2if (result.status === 'granted') {3 await window.dstrnBridge.call('notification.send', {4 title: 'update available',5 body: 'a new version is ready',6 });7}
lifecycle events
the runtime emits events when the application state changes:
1window.dstrnBridge.on('app:foreground', () => {2 console.log('app resumed');3});4 5window.dstrnBridge.on('app:background', () => {6 console.log('app paused');7});8 9window.dstrnBridge.on('app:terminate', () => {10 console.log('app closing');11});
creating plugins
plugin structure
plugins extend the bridge with custom native functionality. each plugin consists of three platform specific implementations that expose identical javascript apis.
a plugin named camera would provide methods like camera.capture and camera.permissions that work consistently across all platforms despite different underlying implementations.
scaffolding a plugin
generate a plugin template using the cli:
1dstrn make:plugin camera
this creates platform specific files in your project:
1native/plugins/2├── camera.swift # ios3├── camera.kt # android4└── camera.rs # desktopeach file contains boilerplate with method registration and example implementations.
ios implementation
ios plugins extend the bridge class with new methods:
1extension Bridge {2 @objc func cameraCapture(_ data: [String: Any], _ callback: @escaping RCTResponseSenderBlock) {3 let picker = UIImagePickerController()4 picker.sourceType = .camera5 // implementation6 callback([["success": true]])7 }8}
android implementation
android plugins add methods to the bridge class:
1class Bridge {2 @JavascriptInterface3 fun cameraCapture(data: String): String {4 val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)5 // implementation6 return JSONObject().put("success", true).toString()7 }8}
desktop implementation
desktop plugins use tauri commands:
1#[tauri::command]2fn camera_capture(data: Value) -> Result<Value, String> {3 // implementation4 Ok(json!({"success": true}))5}
plugin registration
after creating platform implementations, rebuild your native runtimes. the framework automatically discovers and registers plugin methods during the build process.
call plugin methods using the same bridge api:
1const photo = await window.dstrnBridge.call('camera.capture', { 2 quality: 0.8 3});plugins can emit custom events that your javascript code listens for:
1window.dstrnBridge.on('camera:captured', (data) => {2 console.log('photo saved:', data.path);3});
