Skip to content

Conversation

@riderx
Copy link

@riderx riderx commented Oct 29, 2025

Summary

This PR introduces a bidirectional blob transfer system that enables efficient binary data transfer between native code and JavaScript without base64 encoding overhead. This provides ~99% reduction in bridge traffic for large binary data like images, videos, and files.

Motivation

Currently, Capacitor transfers binary data via base64 encoding, which:

  • Increases payload size by 33%
  • Requires CPU-intensive encoding/decoding
  • Causes memory pressure and UI blocking
  • Is particularly slow for large files

This PR was inspired by capacitor-blob-writer but improves upon it by integrating directly into the core framework.

Features

1. Native → JavaScript (Return Blobs from Plugins)

iOS:

@objc func getImage(_ call: CAPPluginCall) {
    let imageData = // ... load image
    call.resolveWithBlob(data: imageData, mimeType: "image/png")
}

Android:

@PluginMethod
public void getImage(PluginCall call) {
    byte[] imageData = // ... load image
    call.resolveWithBlob(imageData, "image/png");
}

JavaScript:

const { blob } = await MyPlugin.getImage();
img.src = blob; // Direct use, no decoding!

2. JavaScript → Native (Send Blobs to Plugins)

JavaScript:

const canvas = document.querySelector('canvas');
const blob = await new Promise(resolve => canvas.toBlob(resolve));
const blobUrl = URL.createObjectURL(blob);
await MyPlugin.saveImage({ blob: blobUrl });

iOS:

@objc func saveImage(_ call: CAPPluginCall) {
    call.getBlobData(for: "blob") { data, mimeType, error in
        // Automatically handles both Capacitor and browser blob URLs
        saveToFile(data)
    }
}

Android:

@PluginMethod
public void saveImage(PluginCall call) {
    call.getBlobData("blob", new BlobDataCallback() {
        public void onSuccess(byte[] data, String mimeType) {
            saveToFile(data);
        }
    });
}

Implementation Details

Core Components

iOS:

  • `CAPBlobStore` - Thread-safe in-memory blob storage
  • `CAPBlobURLSchemeHandler` - Intercepts `blob:capacitor://` URLs
  • Extensions to `CAPPluginCall` for blob methods

Android:

  • `BlobStore` - Concurrent blob storage manager
  • Extensions to `PluginCall` for blob methods
  • JavaScript evaluation for fetching browser blobs

Blob URL Format

```
blob:capacitor://
```

Example: `blob:capacitor://a3d5e7f9-1234-5678-90ab-cdef12345678`

Automatic Source Detection

The system automatically handles:

  • Capacitor blobs (`blob:capacitor://...`) - Retrieved directly from storage
  • Browser blobs (`blob:http://...`) - Fetched via JavaScript evaluation

Memory Management

  • Default lifetime: 5 minutes (configurable)
  • Default size limit: 50MB (configurable)
  • Automatic cleanup on expiration
  • Thread-safe concurrent access
  • Manual removal supported

Security

  • Random UUID-based URLs (non-guessable)
  • App-scoped storage (no cross-app access)
  • Automatic cleanup prevents memory leaks
  • No cross-origin issues

Performance Comparison

Before (Base64)

```
1MB binary → 1.33MB base64 string → JavaScript

  • Encoding overhead
  • Memory duplication
  • UI blocking
    ```

After (Blob Transfer)

```
1MB binary → ~50 byte URL → JavaScript

  • No encoding
  • No memory duplication
  • Non-blocking
    ```

Result: ~99% reduction in bridge traffic

Testing

Comprehensive test suites included:

iOS: `BlobStoreTests.swift` (20+ tests)

  • Basic store/retrieve
  • Large binary data (1MB+)
  • Multiple concurrent blobs
  • Thread safety
  • Memory limits
  • MIME type handling
  • Edge cases

Android: `BlobStoreTest.java` (20+ tests)

  • All iOS test coverage
  • Unicode/binary integrity
  • Concurrent operations
  • Cleanup lifecycle

Documentation

Complete API documentation in `BLOB_TRANSFER_API.md`:

  • API reference for iOS/Android/JavaScript
  • Performance benchmarks
  • Migration guide from base64
  • Best practices
  • Error handling patterns
  • Memory management tips

Backward Compatibility

Fully backward compatible

  • No breaking changes
  • Opt-in API (existing base64 code works unchanged)
  • Can use both approaches simultaneously
  • Progressive migration supported

Files Changed

New Files (7)

  1. `ios/Capacitor/Capacitor/CAPBlobStore.swift`
  2. `ios/Capacitor/Capacitor/CAPBlobURLSchemeHandler.swift`
  3. `android/capacitor/src/main/java/com/getcapacitor/BlobStore.java`
  4. `ios/Capacitor/CapacitorTests/BlobStoreTests.swift`
  5. `android/capacitor/src/test/java/com/getcapacitor/BlobStoreTest.java`
  6. `BLOB_TRANSFER_API.md`

Modified Files (2)

  1. `ios/Capacitor/Capacitor/WebViewDelegationHandler.swift` - Added blob URL handling
  2. `android/capacitor/src/main/java/com/getcapacitor/PluginCall.java` - Added blob helper methods

Use Cases

Perfect for plugins handling:

  • 📸 Camera/Photo Gallery
  • 📄 File operations
  • 🎵 Audio recording/playback
  • 🎥 Video processing
  • 📊 Large data exports
  • 🖼️ Image manipulation
  • 📦 Archive operations

Example Plugins That Would Benefit

  • `@capacitor/camera`
  • `@capacitor/filesystem`
  • `@capacitor-community/media`
  • Any plugin handling large binary data

Migration Example

Before

@objc func getImage(_ call: CAPPluginCall) {
    let imageData = ... 
    let base64 = imageData.base64EncodedString() // Slow for large images
    call.resolve(["data": base64])
}

After

@objc func getImage(_ call: CAPPluginCall) {
    let imageData = ... 
    call.resolveWithBlob(data: imageData, mimeType: "image/png") // Fast!
}

Next Steps

To complete integration:

  1. Register `CAPBlobURLSchemeHandler` in WebView configuration
  2. Update TypeScript type definitions
  3. Consider updating official plugins to use blob transfer

Related


gwdp and others added 15 commits October 29, 2025 07:36
…issing headers)), add notifications at app level for file download statuses and path
…le picker and duplex stream for javascript proxy write
…inary data handling

- Added `resolveWithBlob` methods in `PluginCall.java` to return binary data as blob URLs.
- Introduced `getBlobData` method for fetching binary data from blob URLs.
- Created `BlobStore` class in iOS to manage temporary blob storage with size limits and cleanup.
- Implemented `CAPBlobURLSchemeHandler` to intercept and serve Capacitor blob URLs.
- Enhanced `WebViewDelegationHandler` to allow blob URL requests.
- Developed comprehensive unit tests for blob storage functionality in both Android and iOS.
- Added support for additional fields in blob responses.
- Ensured thread safety and proper handling of edge cases in blob storage operations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants