diff --git a/react-native-expo/App.tsx b/react-native-expo/App.tsx index 5ca482c1a..8172582c9 100644 --- a/react-native-expo/App.tsx +++ b/react-native-expo/App.tsx @@ -14,6 +14,7 @@ import { Ditto, DittoConfig, DittoConfigConnect, + init, StoreObserver, SyncSubscription, } from "@dittolive/ditto"; @@ -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; diff --git a/react-native-expo/MIGRATION-EXPO-V5.md b/react-native-expo/MIGRATION-EXPO-V5.md new file mode 100644 index 000000000..a0a3cdbd9 --- /dev/null +++ b/react-native-expo/MIGRATION-EXPO-V5.md @@ -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 +``` diff --git a/react-native-expo/README.md b/react-native-expo/README.md index ae179ef87..0b13b673f 100644 --- a/react-native-expo/README.md +++ b/react-native-expo/README.md @@ -5,6 +5,12 @@ Ditto is already installed. ⚠️ Expo Go and Expo Web are not 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 diff --git a/react-native-expo/android/app/build.gradle b/react-native-expo/android/app/build.gradle index f967e0f82..830b84f6e 100644 --- a/react-native-expo/android/app/build.gradle +++ b/react-native-expo/android/app/build.gradle @@ -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() diff --git a/react-native-expo/android/app/src/main/AndroidManifest.xml b/react-native-expo/android/app/src/main/AndroidManifest.xml index fd0626992..73e838927 100644 --- a/react-native-expo/android/app/src/main/AndroidManifest.xml +++ b/react-native-expo/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + @@ -13,10 +13,10 @@ - + - + @@ -28,7 +28,7 @@ - + diff --git a/react-native-expo/android/app/src/main/java/com/anonymous/reactnativeexpo/MainApplication.kt b/react-native-expo/android/app/src/main/java/com/anonymous/reactnativeexpo/MainApplication.kt index 22edf6241..94f077339 100644 --- a/react-native-expo/android/app/src/main/java/com/anonymous/reactnativeexpo/MainApplication.kt +++ b/react-native-expo/android/app/src/main/java/com/anonymous/reactnativeexpo/MainApplication.kt @@ -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 = - 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() diff --git a/react-native-expo/android/app/src/main/res/values/colors.xml b/react-native-expo/android/app/src/main/res/values/colors.xml index f387b9011..26ddd5387 100644 --- a/react-native-expo/android/app/src/main/res/values/colors.xml +++ b/react-native-expo/android/app/src/main/res/values/colors.xml @@ -2,5 +2,4 @@ #ffffff #ffffff #023c69 - #ffffff \ No newline at end of file diff --git a/react-native-expo/android/app/src/main/res/values/strings.xml b/react-native-expo/android/app/src/main/res/values/strings.xml index eaeeda929..92708222f 100644 --- a/react-native-expo/android/app/src/main/res/values/strings.xml +++ b/react-native-expo/android/app/src/main/res/values/strings.xml @@ -2,5 +2,4 @@ react-native-expo automatic contain - false \ No newline at end of file diff --git a/react-native-expo/android/app/src/main/res/values/styles.xml b/react-native-expo/android/app/src/main/res/values/styles.xml index 45a97e61b..1a134df18 100644 --- a/react-native-expo/android/app/src/main/res/values/styles.xml +++ b/react-native-expo/android/app/src/main/res/values/styles.xml @@ -1,9 +1,9 @@