Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.{kt,kts}]
indent_size = 4
max_line_length = 120
ktlint_standard_function-naming = disabled
ktlint_standard_no-wildcard-imports = disabled
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,25 @@ jobs:

- name: Check Swift formatting
run: npm run format:swift:check

# TODO: Add ktlint back at some point (it takes over 2 minutes to install)
# ktlint:
# name: ktlint Check
# runs-on: ubuntu-latest
# steps:
# - name: Checkout code
# uses: actions/checkout@v4

# - name: Set up Node.js
# uses: actions/setup-node@v4
# with:
# node-version: '22'
# cache: 'npm'

# - name: Install ktlint
# run: |
# eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
# brew install ktlint

# - name: Check Kotlin formatting
# run: npm run format:kotlin:check
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ xcuserdata/
## Playground app
/example/ios

# Android/IJ
#
.classpath
.cxx
.gradle
.idea
.project
.settings
local.properties
android.iml
android/app/libs
android/keystores/debug.keystore
build/

## Node.js
node_modules/
npm-debug.log
Expand Down
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ __tests__
/.prettierignore
/.prettierrc
/.swiftformat
/.editorconfig

# Android build artifacts
/android/src/androidTest/
Expand Down
21 changes: 12 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
![voltra-banner](https://use-voltra.dev/voltra-baner.jpg)

### Build Live Activities with JSX in React Native
### Build Live Activities and Widgets with JSX in React Native

[![mit licence][license-badge]][license] [![npm downloads][npm-downloads-badge]][npm-downloads] [![PRs Welcome][prs-welcome-badge]][prs-welcome]

Voltra turns React Native JSX into SwiftUI so you can ship custom Live Activities, Dynamic Island layouts without touching Xcode. Author everything in React, keep hot reload, and let the config plugin handle the extension targets.
Voltra turns React Native JSX into SwiftUI and Jetpack Compose Glance so you can ship custom Live Activities, Dynamic Island layouts, and Android Widgets without touching native code. Author everything in React, keep hot reload, and let the config plugin handle the native extension targets.

## Features

- **Ship Native iOS Surfaces**: Create Live Activities, Dynamic Island variants, and static widgets directly from React components - no Swift or Xcode required.
- **Ship Native Surfaces**: Create iOS Live Activities, Dynamic Island variants, and Android Home Screen widgets directly from React components - no Swift, Kotlin, or Xcode/Android Studio UI work required.

- **Fast Development Workflow**: Hooks respect Fast Refresh and both JS and native layers enforce ActivityKit payload budgets.
- **Fast Development Workflow**: Hooks respect Fast Refresh and both JS and native layers enforce platform-specific payload budgets.

- **Production-Ready Push Notifications**: Collect ActivityKit push tokens and push-to-start tokens, stream lifecycle updates, and build server-driven refreshes.
- **Production-Ready Push Notifications**: Support for ActivityKit push tokens (iOS) and FCM (Android) to stream lifecycle updates and build server-driven refreshes.

- **Familiar Styling**: Use React Native style props and ordered SwiftUI modifiers in one place.
- **Familiar Styling**: Use React Native style props and platform-native modifiers (SwiftUI/Glance) in one place.

- **Type-Safe & Developer-Friendly**: The Voltra schema, hooks, and examples ship with TypeScript definitions, tests, and docs so AI coding agents stay productive.

- **Works With Your Setup**: Compatible with Expo Dev Client and bare React Native projects. The config plugin automatically wires iOS extension targets for you.
- **Works With Your Setup**: Compatible with Expo Dev Client and bare React Native projects. The config plugin automatically wires native extension targets for you.

## Documentation

Expand Down Expand Up @@ -50,7 +50,7 @@ Add the config plugin to your `app.json`:
}
```

Then run `npx expo prebuild --clean` to generate the iOS extension target.
Then run `npx expo prebuild --clean` to generate the native extension targets.

See the [documentation](https://use-voltra.dev/getting-started/quick-start) for detailed setup instructions.

Expand Down Expand Up @@ -83,7 +83,10 @@ export function OrderTracker({ orderId }: { orderId: string }) {

## Platform compatibility

**Note:** This module is intended for use on **iOS devices only**.
Voltra is a cross-platform library that supports:

- **iOS**: Live Activities and Dynamic Island (SwiftUI).
- **Android**: Home Screen Widgets (Jetpack Compose Glance).

## Authors

Expand Down
84 changes: 84 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
buildscript {
// 1. ADD THIS BLOCK TO RESOLVE THE PLUGIN
repositories {
google()
mavenCentral()
}
dependencies {
// Ensure this version matches your project's Kotlin version (e.g., 2.0.0, 2.0.20)
classpath "org.jetbrains.kotlin:compose-compiler-gradle-plugin:2.0.0"
classpath "org.jetbrains.kotlin:kotlin-serialization:2.0.21"
}

// Existing helper
ext.safeExtGet = { prop, fallback ->
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
}

apply plugin: 'com.android.library'
apply plugin: 'org.jetbrains.kotlin.plugin.compose'
apply plugin: 'org.jetbrains.kotlin.plugin.serialization'

group = 'voltra'
version = '0.1.0'

def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
apply from: expoModulesCorePlugin
applyKotlinExpoModulesCorePlugin()
useCoreDependencies()
useExpoPublishing()

// If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
// The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
// Most of the time, you may like to manage the Android SDK versions yourself.
def useManagedAndroidSdkVersions = false
if (useManagedAndroidSdkVersions) {
useDefaultAndroidSdkVersions()
} else {
buildscript {
// Simple helper that allows the root project to override versions declared by this library.
ext.safeExtGet = { prop, fallback ->
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
}
project.android {
compileSdkVersion safeExtGet("compileSdkVersion", 36)
defaultConfig {
minSdkVersion safeExtGet("minSdkVersion", 31)
targetSdkVersion safeExtGet("targetSdkVersion", 36)
}
}
}

android {
namespace "voltra"
defaultConfig {
versionCode 1
versionName "0.1.0"
}
lintOptions {
abortOnError false
}
buildFeatures {
compose true
}
}

dependencies {
// Jetpack Glance - use 'api' instead of 'implementation' to make these available to consuming apps
api "androidx.glance:glance:1.2.0-rc01"
api "androidx.glance:glance-appwidget:1.2.0-rc01"

// Compose runtime (required for Glance)
api "androidx.compose.runtime:runtime:1.6.8"

// JSON parsing
implementation "com.google.code.gson:gson:2.10.1"

// Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1"

// Kotlinx Serialization
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
}
15 changes: 15 additions & 0 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

<application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.voltra.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/voltra_file_paths" />
</provider>
</application>
</manifest>
Loading
Loading