refactor: migrate native module from Expo Modules API to React Native Turbo Modules#149
Open
V3RON wants to merge 9 commits into
Open
refactor: migrate native module from Expo Modules API to React Native Turbo Modules#149V3RON wants to merge 9 commits into
V3RON wants to merge 9 commits into
Conversation
… Turbo Modules Closes #148
- Add missing startAndroidLiveUpdate/updateAndroidLiveUpdate/stopAndroidLiveUpdate stubs to VoltraAndroidModuleSpec; these methods existed in the old spec but were never implemented natively — Expo's requireNativeModule masked the mismatch - Extend VoltraModuleSpec with TurboModule in packages/voltra - Fix StyleProp<ViewStyle> vs ViewStyle type on NativeVoltraView style prop
- sendEventWithName -> sendEvent(withName:body:) per React Native rename - VoltraErrors was nested inside the old Expo Module class; move it to a top-level enum so VoltraModuleImpl can reference it without the module prefix
VoltraRNNativeComponent.ts was being processed by CodeGen for both platforms. On Android, the generated Fabric component auto-registration collided with the VoltraRNManager registered via VoltraPackage.createViewManagers, producing "Tried to register two views with the same name VoltraRN". Renaming to .ios.ts ensures CodeGen generates the component descriptor headers only for iOS (needed by VoltraRNComponentView.mm), while Android continues to use the SimpleViewManager path without any conflicting auto-registration.
The Podfile snippet for the widget extension target was using expo-modules-autolinking to locate the voltra package, which stopped working after expo-module.config.json was removed. Replace with standard Node module resolution (require.resolve) which works for both npm workspaces and regular installs. Also expose ./package.json in the voltra exports map so it can be resolved, and add the iOS podspecPath to react-native.config.js for RN autolinking.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why this matters for Voltra users
Until now, Voltra required
expo-modules-coreas part of its native layer. This was invisible to Expo users, but it meant that any React Native project not using Expo had to drag in the entire Expo Modules runtime just to use Voltra — a significant and surprising dependency for something as self-contained as Live Activities and home screen widgets.This PR removes that constraint. After this change, Voltra works in any React Native project, Expo or bare, with no Expo runtime required at all. The Expo Config Plugin (
app.plugin.js) is unaffected — it's a build-time tool and stays as-is.Closes #148
What changed
TypeScript / JS
NativeVoltraModule.ios.tsandNativeVoltraModule.android.ts— processed at build time byreact-native-codegento generate the native protocol/interface for each platformVoltraRNNativeComponent.tsfor theVoltraRNviewrequireNativeModule(Expo) →TurboModuleRegistry.getEnforcing(React Native) in allVoltraModule.tsfilesrequireNativeView(Expo) →requireNativeComponent(React Native) in allVoltraView.tsxfilesaddListener(event, listener)→NativeEventEmitterpattern in allevents.tsfilesexporemoved from peer dependencies inios-client,android-client, andvoltracodegenConfigadded topackages/voltra/package.jsonexpo-module.config.jsondeletediOS
VoltraModule.swiftrewritten asRCTEventEmitterwith@objcmethods andTask-based async/await bridgingVoltraModuleBridge.mcreated —RCT_EXTERN_MODULE+RCT_EXTERN_METHODdeclarations that register the Swift class with React NativeVoltraRNComponentView.mmcreated — ObjC++ Fabric bridge connecting the codegen-generatedRCTViewComponentViewto the SwiftVoltraRNviewVoltraRN.swiftchanged fromExpoViewto plainUIView(@objc public)VoltraOptions.swift,VoltraImagePreload.swift,VoltraModuleImpl.swift— replacedRecord/@Field(Expo auto-deserialization) with plain Swift structs that parse fromNSDictionaryat the module boundary and serialize back viatoDictionary()Voltra.podspec— replacedExpoModulesCorewithReact-Core+React-RCTFabric, added.m/.mmto source filesAndroid
VoltraModule.ktrewritten asReactContextBaseJavaModulewith@ReactMethodannotations; async methods take aPromiseparameter; sync query methods useisBlockingSynchronousMethod = trueVoltraRNBridgeExtensions.ktcreated —ReadableMapbuilder functions andWritableMapserializers for notification option/result types, keeping the module code free of raw map accessVoltraRN.ktchanged fromExpoViewto plainFrameLayout;AppContextdependency removedVoltraRNManager.ktcreated —SimpleViewManagerexposingpayloadandviewIdvia@ReactPropVoltraPackage.ktcreated —TurboReactPackagethat registers both the module and the view managerreact-native.config.jscreated — configures React Native autolinking for the packagebuild.gradleupdated — Expo plugin machinery removed,com.facebook.react:react-androidaddedTest plan
VoltraViewrenders correctly inlineNativeEventEmitterVoltraViewrenders correctly inlinevoltrawithout Expo and confirm noexpo-modules-corein the dependency tree