Skip to content
Draft
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
3 changes: 3 additions & 0 deletions react-native-expo/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
Ditto,
DittoConfig,
DittoConfigConnect,
init,
StoreObserver,
SyncSubscription,
} from "@dittolive/ditto";
Expand Down Expand Up @@ -119,6 +120,8 @@ const App = () => {

const initDitto = async () => {
try {
await init();

// https://docs.ditto.live/sdk/latest/install-guides/react-native#onlineplayground
const databaseId = DITTO_APP_ID;
const playgroundToken = DITTO_PLAYGROUND_TOKEN;
Expand Down
72 changes: 72 additions & 0 deletions react-native-expo/MIGRATION-EXPO-V5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Ditto SDK v5 Migration — Expo-Specific Requirements

These are the changes required specifically for Expo projects when migrating from Ditto SDK v4 to v5. These are in addition to the general API migration steps (DittoConfig, auth, sync, DQL, etc.).

## 1. Add `expo-build-properties` dependency

The v5 Ditto Expo plugin internally calls `withBuildProperties()` from `expo-build-properties`. If this package is not installed, `expo prebuild` will fail with an import error.

```bash
npm install expo-build-properties@~1.0.0
```

## 2. Configure Kotlin version in `app.json`

The v5 SDK requires Kotlin 2.1.20 for Android builds. Add the `expo-build-properties` plugin to your `app.json` with the Kotlin version:

```json
{
"expo": {
"plugins": [
[
"expo-build-properties",
{
"android": {
"kotlinVersion": "2.1.20"
}
}
]
]
}
}
```

Without this, Android builds will fail with Kotlin compilation errors.

## 3. Call `init()` before using any Ditto API

v5 exports an `init()` function that must be called before creating a Ditto instance. On React Native this is effectively a no-op (it only loads WebAssembly on web), but all v5 examples call it and it is the recommended pattern.

```typescript
import { Ditto, DittoConfig, init } from '@dittolive/ditto';

async function startDitto() {
await init();
const config = new DittoConfig(databaseId, connectConfig);
const ditto = await Ditto.open(config);
// ...
}
```

## 4. Regenerate native projects

After making the above changes, delete the existing `ios` and `android` directories and regenerate them:

```bash
rm -rf ios android
npx expo prebuild
```

The v5 Expo plugin automatically handles:

- **iOS**: `NSBluetoothAlwaysUsageDescription`, `NSLocalNetworkUsageDescription`, Bonjour services, and background modes in `Info.plist`
- **Android**: Bluetooth/WiFi/location permissions in `AndroidManifest.xml` and `packagingOptions.pickFirsts` for native libraries (`libdittoffi.so`, `libjsi.so`, `libreact_nativemodule_core.so`, `libturbomodulejsijni.so`, `libreactnative.so`)

## 5. Update `yarn.lock` (if present)

If your project has both `package-lock.json` and `yarn.lock`, update both after adding the new dependency:

```bash
npm install
npx yarn install
```
6 changes: 6 additions & 0 deletions react-native-expo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ Ditto is already installed.

⚠️ Expo Go and Expo Web are <b>not</b> compatible with Ditto.

## Requirements

- **Xcode 26.2 or lower** — `@dittolive/ditto@5.0.0-rc.2` bundles `fmt 11.0.2` which has a `consteval` compilation bug with Xcode 26.4's Clang. Builds will fail on Xcode 26.3+.
- **Gradle 9.0+** — Included automatically via the Gradle wrapper.
- **JDK 17** — Required for Android builds. Install via `brew install openjdk@17` and ensure `JAVA_HOME` is set.

## Get started

1. Install dependencies
Expand Down
2 changes: 1 addition & 1 deletion react-native-expo/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath()
react {
entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim())
reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc"
hermesCommand = new File(["node", "--print", "require.resolve('hermes-compiler/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/hermesc/%OS-BIN%/hermesc"
codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()

enableBundleCompression = (findProperty('android.enableBundleCompression') ?: false).toBoolean()
Expand Down
8 changes: 4 additions & 4 deletions react-native-expo/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
Expand All @@ -13,10 +13,10 @@
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" android:usesPermissionFlags="neverForLocation"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" tools:replace="android:maxSdkVersion"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" tools:replace="android:maxSdkVersion"/>
<queries>
<intent>
<action android:name="android.intent.action.VIEW"/>
Expand All @@ -28,7 +28,7 @@
<meta-data android:name="expo.modules.updates.ENABLED" android:value="false"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode|smallestScreenSize" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,26 @@ import android.content.res.Configuration
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.ReactHost
import com.facebook.react.common.ReleaseLevel
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
import com.facebook.react.defaults.DefaultReactNativeHost

import expo.modules.ApplicationLifecycleDispatcher
import expo.modules.ReactNativeHostWrapper
import expo.modules.ExpoReactHostFactory

class MainApplication : Application(), ReactApplication {

override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper(
this,
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
}

override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry"

override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG

override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
}
)

override val reactHost: ReactHost
get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost)
override val reactHost: ReactHost by lazy {
ExpoReactHostFactory.getDefaultReactHost(
context = applicationContext,
packageList =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
}
)
}

override fun onCreate() {
super.onCreate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@
<color name="splashscreen_background">#ffffff</color>
<color name="iconBackground">#ffffff</color>
<color name="colorPrimary">#023c69</color>
<color name="colorPrimaryDark">#ffffff</color>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@
<string name="app_name">react-native-expo</string>
<string name="expo_system_ui_user_interface_style" translatable="false">automatic</string>
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:enforceNavigationBarContrast" tools:targetApi="29">true</item>
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="android:statusBarColor">#ffffff</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="Theme.App.SplashScreen" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/splashscreen_background</item>
Expand Down
8 changes: 2 additions & 6 deletions react-native-expo/android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,5 @@ EX_DEV_CLIENT_NETWORK_INSPECTOR=true
# Use legacy packaging to compress native libraries in the resulting APK.
expo.useLegacyPackaging=false

# Specifies whether the app is configured to use edge-to-edge via the app config or plugin
# WARNING: This property has been deprecated and will be removed in Expo SDK 55. Use `edgeToEdgeEnabled` or `react.edgeToEdgeEnabled` to determine whether the project is using edge-to-edge.
expo.edgeToEdgeEnabled=true

android.packagingOptions.pickFirsts=lib/x86/libjsi.so,lib/x86/libdittoffi.so,lib/x86/libreact_nativemodule_core.so,lib/x86/libturbomodulejsijni.so,lib/x86/libreactnative.so,lib/x86_64/libjsi.so,lib/x86_64/libdittoffi.so,lib/x86_64/libreact_nativemodule_core.so,lib/x86_64/libturbomodulejsijni.so,lib/x86_64/libreactnative.so,lib/armeabi-v7a/libjsi.so,lib/armeabi-v7a/libdittoffi.so,lib/armeabi-v7a/libreact_nativemodule_core.so,lib/armeabi-v7a/libturbomodulejsijni.so,lib/armeabi-v7a/libreactnative.so,lib/arm64-v8a/libjsi.so,lib/arm64-v8a/libdittoffi.so,lib/arm64-v8a/libreact_nativemodule_core.so,lib/arm64-v8a/libturbomodulejsijni.so,lib/arm64-v8a/libreactnative.so
android.extraMavenRepos=[]
android.kotlinVersion=2.1.20
android.packagingOptions.pickFirsts=lib/x86/libjsi.so,lib/x86/libdittoffi.so,lib/x86/libreact_nativemodule_core.so,lib/x86/libturbomodulejsijni.so,lib/x86/libreactnative.so,lib/x86_64/libjsi.so,lib/x86_64/libdittoffi.so,lib/x86_64/libreact_nativemodule_core.so,lib/x86_64/libturbomodulejsijni.so,lib/x86_64/libreactnative.so,lib/armeabi-v7a/libjsi.so,lib/armeabi-v7a/libdittoffi.so,lib/armeabi-v7a/libreact_nativemodule_core.so,lib/armeabi-v7a/libturbomodulejsijni.so,lib/armeabi-v7a/libreactnative.so,lib/arm64-v8a/libjsi.so,lib/arm64-v8a/libdittoffi.so,lib/arm64-v8a/libreact_nativemodule_core.so,lib/arm64-v8a/libturbomodulejsijni.so,lib/arm64-v8a/libreactnative.so
Binary file modified react-native-expo/android/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
2 changes: 1 addition & 1 deletion react-native-expo/android/gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions react-native-expo/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"package": "com.anonymous.reactnativeexpo",
"edgeToEdgeEnabled": true,
"predictiveBackGestureEnabled": false
"package": "com.anonymous.reactnativeexpo"
},
"web": {
"bundler": "metro",
Expand Down Expand Up @@ -47,6 +45,14 @@
"localNetworkUsageDescription": "This app uses location services to connect to a Ditto device."
}
],
[
"expo-build-properties",
{
"android": {
"kotlinVersion": "2.1.20"
}
}
],
"expo-font",
"expo-web-browser"
],
Expand Down
15 changes: 10 additions & 5 deletions react-native-expo/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Set by expo-router. This enables Fabric-only features from react-native-screens
ENV['RNS_GAMMA_ENABLED'] ||= '1'
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")

Expand All @@ -7,15 +9,15 @@ podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties
def ccache_enabled?(podfile_properties)
# Environment variable takes precedence
return ENV['USE_CCACHE'] == '1' if ENV['USE_CCACHE']

# Fall back to Podfile properties
podfile_properties['apple.ccacheEnabled'] == 'true'
end

ENV['RCT_NEW_ARCH_ENABLED'] ||= '0' if podfile_properties['newArchEnabled'] == 'false'
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] ||= podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
ENV['RCT_USE_RN_DEP'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false'
ENV['RCT_USE_PREBUILT_RNCORE'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false'
ENV['RCT_USE_RN_DEP'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true'
ENV['RCT_USE_PREBUILT_RNCORE'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true'
ENV['RCT_HERMES_V1_ENABLED'] ||= '1' if podfile_properties['expo.useHermesV1'] == 'true'
platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1'

prepare_react_native_project!
Expand All @@ -27,7 +29,10 @@ target 'reactnativeexpo' do
config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"];
else
config_command = [
'npx',
'node',
'--no-warnings',
'--eval',
'require(\'expo/bin/autolinking\')',
'expo-modules-autolinking',
'react-native-config',
'--json',
Expand Down
Loading
Loading