From cd3b1ca7a9024a8052af0c7410c4ffcda9df9cf0 Mon Sep 17 00:00:00 2001 From: Artem Litchmanov Date: Sat, 17 Oct 2026 03:00:30 -0700 Subject: [PATCH 1/2] iOS: callAsyncJavaScript module method and an experimental fast message handler callAsyncJavaScript(viewTag, functionBody, argument) runs a function in the page and resolves with its string result, so a caller gets a reply without a postMessage and onMessage round trip. The ReactNativeWebViewFast handler delivers page messages through the module's onFastMessage emitter; it measured slower than onMessage and is unused. Co-Authored-By: Claude Opus 5.5 --- .../webview/RNCWebViewModule.java | 5 ++ apple/RNCWebView.mm | 11 ++++ apple/RNCWebViewImpl.h | 7 +++ apple/RNCWebViewImpl.m | 54 +++++++++++++++++++ apple/RNCWebViewModule.h | 6 +-- apple/RNCWebViewModule.mm | 35 ++++++++++++ lib/WebView.ios.js | 2 +- src/NativeRNCWebViewModule.ts | 16 +++++- src/WebView.ios.tsx | 15 +++++- 9 files changed, 145 insertions(+), 6 deletions(-) diff --git a/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewModule.java b/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewModule.java index 4df0aae4f6..3d34f0245e 100644 --- a/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewModule.java +++ b/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewModule.java @@ -29,6 +29,11 @@ public void shouldStartLoadWithLockIdentifier(boolean shouldStart, double lockId mRNCWebViewModuleImpl.shouldStartLoadWithLockIdentifier(shouldStart, lockIdentifier); } + @Override + public void callAsyncJavaScript(double viewTag, String functionBody, String argument, final Promise promise) { + promise.reject("E_UNSUPPORTED", "callAsyncJavaScript is iOS-only"); + } + public void startPhotoPickerIntent(ValueCallback filePathCallback, String acceptType) { mRNCWebViewModuleImpl.startPhotoPickerIntent(acceptType, filePathCallback); } diff --git a/apple/RNCWebView.mm b/apple/RNCWebView.mm index ff07216818..85f8d75bd7 100644 --- a/apple/RNCWebView.mm +++ b/apple/RNCWebView.mm @@ -67,6 +67,17 @@ + (void)load [super load]; } +- (void)setTag:(NSInteger)tag +{ + if (self.tag != 0 && [RNCWebViewImpl implForReactTag:self.tag] == _view) { + [RNCWebViewImpl registerImpl:nil forReactTag:self.tag]; + } + [super setTag:tag]; + if (tag != 0) { + [RNCWebViewImpl registerImpl:_view forReactTag:tag]; + } +} + - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { diff --git a/apple/RNCWebViewImpl.h b/apple/RNCWebViewImpl.h index c1c27b4332..9d8787fa67 100644 --- a/apple/RNCWebViewImpl.h +++ b/apple/RNCWebViewImpl.h @@ -165,6 +165,13 @@ shouldStartLoadForRequest:(NSMutableDictionary *)request + (void)setCustomCertificatesForHost:(nullable NSDictionary *)certificates; - (void)postMessage:(NSString *_Nullable)message; - (void)injectJavaScript:(NSString *_Nullable)script; +- (void)callAsyncJavaScript:(NSString *_Nonnull)functionBody + argument:(NSString *_Nullable)argument + completion:(void (^_Nonnull)(id _Nullable result, NSError *_Nullable error))completion; ++ (nullable RNCWebViewImpl *)implForReactTag:(NSInteger)reactTag; ++ (void)registerImpl:(nullable RNCWebViewImpl *)impl forReactTag:(NSInteger)reactTag; ++ (void)setFastMessageSink:(nullable void (^)(NSInteger reactTag, NSString *_Nonnull data))sink; +@property (nonatomic, assign) NSInteger fastMessageReactTag; - (void)goForward; - (void)goBack; - (void)reload; diff --git a/apple/RNCWebViewImpl.m b/apple/RNCWebViewImpl.m index 7056a39cea..df35ed1f36 100644 --- a/apple/RNCWebViewImpl.m +++ b/apple/RNCWebViewImpl.m @@ -26,6 +26,8 @@ static NSString *const MessageHandlerName = @"ReactNativeWebView"; static NSString *const PageSpacersMessageHandlerName = @"pageSpacers"; static NSString *const PageCurlMessageHandlerName = @"pageCurl"; +static NSString *const FastMessageHandlerName = @"ReactNativeWebViewFast"; +static void (^RNCWebViewFastMessageSink)(NSInteger reactTag, NSString *data); static NSURLCredential* clientAuthenticationCredential; static NSDictionary* customCertificatesForHost; @@ -504,6 +506,8 @@ - (WKWebViewConfiguration *)setUpWkWebViewConfig // Shim the HTML5 history API: [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self] name:HistoryShimName]; + [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self] + name:FastMessageHandlerName]; [self resetupScripts:wkWebViewConfig]; if(@available(macos 10.11, ios 9.0, *)) { @@ -643,6 +647,7 @@ - (void)removeFromSuperview { if (_webView) { [_webView.configuration.userContentController removeScriptMessageHandlerForName:HistoryShimName]; + [_webView.configuration.userContentController removeScriptMessageHandlerForName:FastMessageHandlerName]; [_webView.configuration.userContentController removeScriptMessageHandlerForName:MessageHandlerName]; [_webView removeObserver:self forKeyPath:@"estimatedProgress"]; [_webView removeFromSuperview]; @@ -925,6 +930,10 @@ - (void)userContentController:(WKUserContentController *)userContentController [event addEntriesFromDictionary: @{@"navigationType": message.body}]; _onLoadingFinish(event); } + } else if ([message.name isEqualToString:FastMessageHandlerName]) { + if (RNCWebViewFastMessageSink && _fastMessageReactTag != 0 && [message.body isKindOfClass:[NSString class]]) { + RNCWebViewFastMessageSink(_fastMessageReactTag, message.body); + } } else if ([message.name isEqualToString:MessageHandlerName]) { if (_onMessage) { NSMutableDictionary *event = [self baseEvent]; @@ -1930,6 +1939,51 @@ - (void)injectJavaScript:(NSString *)script [self evaluateJS: script thenCall: nil]; } +static NSMapTable *RNCWebViewImplRegistry(void) +{ + static NSMapTable *registry; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + registry = [NSMapTable strongToWeakObjectsMapTable]; + }); + return registry; +} + ++ (RNCWebViewImpl *)implForReactTag:(NSInteger)reactTag +{ + return [RNCWebViewImplRegistry() objectForKey:@(reactTag)]; +} + ++ (void)setFastMessageSink:(void (^)(NSInteger reactTag, NSString *data))sink +{ + RNCWebViewFastMessageSink = [sink copy]; +} + ++ (void)registerImpl:(RNCWebViewImpl *)impl forReactTag:(NSInteger)reactTag +{ + if (impl) { + impl.fastMessageReactTag = reactTag; + [RNCWebViewImplRegistry() setObject:impl forKey:@(reactTag)]; + } else { + [RNCWebViewImplRegistry() removeObjectForKey:@(reactTag)]; + } +} + +- (void)callAsyncJavaScript:(NSString *)functionBody + argument:(NSString *)argument + completion:(void (^)(id result, NSError *error))completion +{ + if (_webView == nil) { + completion(nil, [NSError errorWithDomain:@"RNCWebView" code:1 userInfo:@{NSLocalizedDescriptionKey: @"webview is not mounted"}]); + return; + } + [_webView callAsyncJavaScript:functionBody + arguments:@{@"argument": argument ?: [NSNull null]} + inFrame:nil + inContentWorld:WKContentWorld.pageWorld + completionHandler:completion]; +} + - (void)goForward { [_webView goForward]; diff --git a/apple/RNCWebViewModule.h b/apple/RNCWebViewModule.h index 586020930e..d58e63d694 100644 --- a/apple/RNCWebViewModule.h +++ b/apple/RNCWebViewModule.h @@ -9,13 +9,13 @@ NS_ASSUME_NONNULL_BEGIN -@interface RNCWebViewModule : NSObject < #ifdef RCT_NEW_ARCH_ENABLED +@interface RNCWebViewModule : NativeRNCWebViewModuleSpecBase < NativeRNCWebViewModuleSpec +> #else -RCTBridgeModule +@interface RNCWebViewModule : NSObject #endif /* RCT_NEW_ARCH_ENABLED */ -> @end NS_ASSUME_NONNULL_END diff --git a/apple/RNCWebViewModule.mm b/apple/RNCWebViewModule.mm index 547b95f513..be8bd89925 100644 --- a/apple/RNCWebViewModule.mm +++ b/apple/RNCWebViewModule.mm @@ -1,6 +1,7 @@ #import "RNCWebViewModule.h" #import "RNCWebViewDecisionManager.h" +#import "RNCWebViewImpl.h" #ifdef RCT_NEW_ARCH_ENABLED #import @@ -10,6 +11,31 @@ @implementation RNCWebViewModule RCT_EXPORT_MODULE(RNCWebViewModule) +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + +RCT_EXPORT_METHOD(callAsyncJavaScript:(double)viewTag + functionBody:(NSString *)functionBody + argument:(NSString *)argument + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) +{ + RNCWebViewImpl *impl = [RNCWebViewImpl implForReactTag:(NSInteger)viewTag]; + if (impl == nil) { + reject(@"E_NO_WEBVIEW", [NSString stringWithFormat:@"no webview with tag %d", (int)viewTag], nil); + return; + } + [impl callAsyncJavaScript:functionBody argument:argument completion:^(id result, NSError *error) { + if (error != nil) { + reject(@"E_JS", error.localizedDescription, error); + return; + } + resolve([result isKindOfClass:[NSString class]] ? result : [NSNull null]); + }]; +} + RCT_EXPORT_METHOD(isFileUploadSupported:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { if (resolve) { resolve(@(YES)); @@ -22,6 +48,15 @@ @implementation RNCWebViewModule } #ifdef RCT_NEW_ARCH_ENABLED +- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper +{ + [super setEventEmitterCallback:eventEmitterCallbackWrapper]; + __weak RNCWebViewModule *weakSelf = self; + [RNCWebViewImpl setFastMessageSink:^(NSInteger reactTag, NSString *data) { + [weakSelf emitOnFastMessage:@{@"viewTag": @(reactTag), @"data": data}]; + }]; +} + - (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { return std::make_shared(params); } diff --git a/lib/WebView.ios.js b/lib/WebView.ios.js index 36dc0f77dd..c8ff676816 100644 --- a/lib/WebView.ios.js +++ b/lib/WebView.ios.js @@ -1 +1 @@ -var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");Object.defineProperty(exports,"__esModule",{value:true});exports.default=void 0;var _asyncToGenerator2=_interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));var _defineProperty2=_interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));var _slicedToArray2=_interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));var _objectWithoutProperties2=_interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));var _react=_interopRequireWildcard(require("react"));var _reactNative=require("react-native");var _invariant=_interopRequireDefault(require("invariant"));var _RNCWebViewNativeComponent=_interopRequireWildcard(require("./RNCWebViewNativeComponent"));var _NativeRNCWebViewModule=_interopRequireDefault(require("./NativeRNCWebViewModule"));var _WebViewShared=require("./WebViewShared");var _WebView=_interopRequireDefault(require("./WebView.styles"));var _jsxRuntime=require("react/jsx-runtime");var _excluded=["fraudulentWebsiteWarningEnabled","javaScriptEnabled","cacheEnabled","originWhitelist","useSharedProcessPool","textInteractionEnabled","injectedJavaScript","injectedJavaScriptBeforeContentLoaded","injectedJavaScriptForMainFrameOnly","injectedJavaScriptBeforeContentLoadedForMainFrameOnly","injectedJavaScriptObject","startInLoadingState","onNavigationStateChange","onLoadStart","onError","onLoad","onLoadEnd","onLoadProgress","onContentProcessDidTerminate","onFileDownload","onHttpError","onMessage","onOpenWindow","renderLoading","renderError","style","containerStyle","source","nativeConfig","allowsInlineMediaPlayback","allowsPictureInPictureMediaPlayback","allowsAirPlayForMediaPlayback","mediaPlaybackRequiresUserAction","dataDetectorTypes","incognito","decelerationRate","onShouldStartLoadWithRequest","pageCurlTuning"];var _this=this,_jsxFileName="/Users/artemlitch/readwise/react-native-webview/src/WebView.ios.tsx";function _interopRequireWildcard(e,t){if("function"==typeof WeakMap)var r=new WeakMap(),n=new WeakMap();return(_interopRequireWildcard=function _interopRequireWildcard(e,t){if(!t&&e&&e.__esModule)return e;var o,i,f={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return f;if(o=t?n:r){if(o.has(e))return o.get(e);o.set(e,f);}for(var _t in e)"default"!==_t&&{}.hasOwnProperty.call(e,_t)&&((i=(o=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,_t))&&(i.get||i.set)?o(f,_t,i):f[_t]=e[_t]);return f;})(e,t);}var resolveAssetSource=_reactNative.Image.resolveAssetSource;var processDecelerationRate=function processDecelerationRate(decelerationRate){var newDecelerationRate=decelerationRate;if(newDecelerationRate==='normal'){newDecelerationRate=0.998;}else if(newDecelerationRate==='fast'){newDecelerationRate=0.99;}return newDecelerationRate;};var useWarnIfChanges=function useWarnIfChanges(value,name){var ref=(0,_react.useRef)(value);if(ref.current!==value){console.warn(`Changes to property ${name} do nothing after the initial render.`);ref.current=value;}};var WebViewComponent=(0,_react.forwardRef)(function(_ref,ref){var _ref$fraudulentWebsit=_ref.fraudulentWebsiteWarningEnabled,fraudulentWebsiteWarningEnabled=_ref$fraudulentWebsit===void 0?true:_ref$fraudulentWebsit,_ref$javaScriptEnable=_ref.javaScriptEnabled,javaScriptEnabled=_ref$javaScriptEnable===void 0?true:_ref$javaScriptEnable,_ref$cacheEnabled=_ref.cacheEnabled,cacheEnabled=_ref$cacheEnabled===void 0?true:_ref$cacheEnabled,_ref$originWhitelist=_ref.originWhitelist,originWhitelist=_ref$originWhitelist===void 0?_WebViewShared.defaultOriginWhitelist:_ref$originWhitelist,_ref$useSharedProcess=_ref.useSharedProcessPool,useSharedProcessPool=_ref$useSharedProcess===void 0?true:_ref$useSharedProcess,_ref$textInteractionE=_ref.textInteractionEnabled,textInteractionEnabled=_ref$textInteractionE===void 0?true:_ref$textInteractionE,injectedJavaScript=_ref.injectedJavaScript,injectedJavaScriptBeforeContentLoaded=_ref.injectedJavaScriptBeforeContentLoaded,_ref$injectedJavaScri=_ref.injectedJavaScriptForMainFrameOnly,injectedJavaScriptForMainFrameOnly=_ref$injectedJavaScri===void 0?true:_ref$injectedJavaScri,_ref$injectedJavaScri2=_ref.injectedJavaScriptBeforeContentLoadedForMainFrameOnly,injectedJavaScriptBeforeContentLoadedForMainFrameOnly=_ref$injectedJavaScri2===void 0?true:_ref$injectedJavaScri2,injectedJavaScriptObject=_ref.injectedJavaScriptObject,startInLoadingState=_ref.startInLoadingState,onNavigationStateChange=_ref.onNavigationStateChange,onLoadStart=_ref.onLoadStart,onError=_ref.onError,onLoad=_ref.onLoad,onLoadEnd=_ref.onLoadEnd,onLoadProgress=_ref.onLoadProgress,onContentProcessDidTerminateProp=_ref.onContentProcessDidTerminate,onFileDownload=_ref.onFileDownload,onHttpErrorProp=_ref.onHttpError,onMessageProp=_ref.onMessage,onOpenWindowProp=_ref.onOpenWindow,renderLoading=_ref.renderLoading,renderError=_ref.renderError,style=_ref.style,containerStyle=_ref.containerStyle,source=_ref.source,nativeConfig=_ref.nativeConfig,allowsInlineMediaPlayback=_ref.allowsInlineMediaPlayback,_ref$allowsPictureInP=_ref.allowsPictureInPictureMediaPlayback,allowsPictureInPictureMediaPlayback=_ref$allowsPictureInP===void 0?true:_ref$allowsPictureInP,allowsAirPlayForMediaPlayback=_ref.allowsAirPlayForMediaPlayback,mediaPlaybackRequiresUserAction=_ref.mediaPlaybackRequiresUserAction,dataDetectorTypes=_ref.dataDetectorTypes,incognito=_ref.incognito,decelerationRateProp=_ref.decelerationRate,onShouldStartLoadWithRequestProp=_ref.onShouldStartLoadWithRequest,pageCurlTuning=_ref.pageCurlTuning,otherProps=(0,_objectWithoutProperties2.default)(_ref,_excluded);var webViewRef=(0,_react.useRef)(null);var onShouldStartLoadWithRequestCallback=(0,_react.useCallback)(function(shouldStart,_url){var lockIdentifier=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0;_NativeRNCWebViewModule.default.shouldStartLoadWithLockIdentifier(shouldStart,lockIdentifier);},[]);var _useWebViewLogic=(0,_WebViewShared.useWebViewLogic)({onNavigationStateChange:onNavigationStateChange,onLoad:onLoad,onError:onError,onHttpErrorProp:onHttpErrorProp,onLoadEnd:onLoadEnd,onLoadProgress:onLoadProgress,onLoadStart:onLoadStart,onMessageProp:onMessageProp,onOpenWindowProp:onOpenWindowProp,startInLoadingState:startInLoadingState,originWhitelist:originWhitelist,onShouldStartLoadWithRequestProp:onShouldStartLoadWithRequestProp,onShouldStartLoadWithRequestCallback:onShouldStartLoadWithRequestCallback,onContentProcessDidTerminateProp:onContentProcessDidTerminateProp}),onLoadingStart=_useWebViewLogic.onLoadingStart,onShouldStartLoadWithRequest=_useWebViewLogic.onShouldStartLoadWithRequest,onMessage=_useWebViewLogic.onMessage,viewState=_useWebViewLogic.viewState,setViewState=_useWebViewLogic.setViewState,lastErrorEvent=_useWebViewLogic.lastErrorEvent,onHttpError=_useWebViewLogic.onHttpError,onLoadingError=_useWebViewLogic.onLoadingError,onLoadingFinish=_useWebViewLogic.onLoadingFinish,onLoadingProgress=_useWebViewLogic.onLoadingProgress,onOpenWindow=_useWebViewLogic.onOpenWindow,onContentProcessDidTerminate=_useWebViewLogic.onContentProcessDidTerminate;(0,_react.useImperativeHandle)(ref,function(){return{goForward:function goForward(){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.goForward(webViewRef.current);},goBack:function goBack(){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.goBack(webViewRef.current);},reload:function reload(){setViewState('LOADING');if(webViewRef.current){_RNCWebViewNativeComponent.Commands.reload(webViewRef.current);}},stopLoading:function stopLoading(){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.stopLoading(webViewRef.current);},postMessage:function postMessage(data){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.postMessage(webViewRef.current,data);},injectJavaScript:function injectJavaScript(data){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.injectJavaScript(webViewRef.current,data);},requestFocus:function requestFocus(){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.requestFocus(webViewRef.current);},clearCache:function clearCache(includeDiskFiles){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.clearCache(webViewRef.current,includeDiskFiles);},setTintColor:function setTintColor(red,green,blue,alpha){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.setTintColor(webViewRef.current,red,green,blue,alpha);}};},[setViewState,webViewRef]);useWarnIfChanges(allowsInlineMediaPlayback,'allowsInlineMediaPlayback');useWarnIfChanges(allowsPictureInPictureMediaPlayback,'allowsPictureInPictureMediaPlayback');useWarnIfChanges(allowsAirPlayForMediaPlayback,'allowsAirPlayForMediaPlayback');useWarnIfChanges(incognito,'incognito');useWarnIfChanges(mediaPlaybackRequiresUserAction,'mediaPlaybackRequiresUserAction');useWarnIfChanges(dataDetectorTypes,'dataDetectorTypes');var otherView=null;if(viewState==='LOADING'){otherView=(renderLoading||_WebViewShared.defaultRenderLoading)();}else if(viewState==='ERROR'){var _lastErrorEvent$code,_lastErrorEvent$descr;(0,_invariant.default)(lastErrorEvent!=null,'lastErrorEvent expected to be non-null');otherView=(renderError||_WebViewShared.defaultRenderError)(lastErrorEvent==null?void 0:lastErrorEvent.domain,(_lastErrorEvent$code=lastErrorEvent==null?void 0:lastErrorEvent.code)!=null?_lastErrorEvent$code:0,(_lastErrorEvent$descr=lastErrorEvent==null?void 0:lastErrorEvent.description)!=null?_lastErrorEvent$descr:'');}else if(viewState!=='IDLE'){console.error(`RNCWebView invalid state encountered: ${viewState}`);}var webViewStyles=[_WebView.default.container,_WebView.default.webView,style];var webViewContainerStyle=[_WebView.default.container,containerStyle];var decelerationRate=processDecelerationRate(decelerationRateProp);var NativeWebView=(nativeConfig==null?void 0:nativeConfig.component)||_RNCWebViewNativeComponent.default;var sourceResolved=resolveAssetSource(source);var newSource=typeof sourceResolved==='object'?Object.entries(sourceResolved).reduce(function(prev,_ref2){var _ref3=(0,_slicedToArray2.default)(_ref2,2),currKey=_ref3[0],currValue=_ref3[1];return Object.assign({},prev,(0,_defineProperty2.default)({},currKey,currKey==='headers'&&currValue&&typeof currValue==='object'?Object.entries(currValue).map(function(_ref4){var _ref5=(0,_slicedToArray2.default)(_ref4,2),key=_ref5[0],value=_ref5[1];return{name:key,value:value};}):currValue));},{}):sourceResolved;var webView=(0,_jsxRuntime.jsx)(NativeWebView,Object.assign({},otherProps,{fraudulentWebsiteWarningEnabled:fraudulentWebsiteWarningEnabled,javaScriptEnabled:javaScriptEnabled,cacheEnabled:cacheEnabled,useSharedProcessPool:useSharedProcessPool,textInteractionEnabled:textInteractionEnabled,decelerationRate:decelerationRate,messagingEnabled:typeof onMessageProp==='function',messagingModuleName:"",onLoadingError:onLoadingError,onLoadingFinish:onLoadingFinish,onLoadingProgress:onLoadingProgress,onFileDownload:onFileDownload,onLoadingStart:onLoadingStart,onHttpError:onHttpError,onMessage:onMessage,onOpenWindow:onOpenWindowProp&&onOpenWindow,hasOnOpenWindowEvent:onOpenWindowProp!==undefined,onShouldStartLoadWithRequest:onShouldStartLoadWithRequest,pageCurlTuning:pageCurlTuning&&JSON.stringify(pageCurlTuning),onContentProcessDidTerminate:onContentProcessDidTerminate,injectedJavaScript:injectedJavaScript,injectedJavaScriptBeforeContentLoaded:injectedJavaScriptBeforeContentLoaded,injectedJavaScriptForMainFrameOnly:injectedJavaScriptForMainFrameOnly,injectedJavaScriptBeforeContentLoadedForMainFrameOnly:injectedJavaScriptBeforeContentLoadedForMainFrameOnly,injectedJavaScriptObject:JSON.stringify(injectedJavaScriptObject),dataDetectorTypes:!dataDetectorTypes||Array.isArray(dataDetectorTypes)?dataDetectorTypes:[dataDetectorTypes],allowsAirPlayForMediaPlayback:allowsAirPlayForMediaPlayback,allowsInlineMediaPlayback:allowsInlineMediaPlayback,allowsPictureInPictureMediaPlayback:allowsPictureInPictureMediaPlayback,incognito:incognito,mediaPlaybackRequiresUserAction:mediaPlaybackRequiresUserAction,newSource:newSource,style:webViewStyles,hasOnFileDownload:!!onFileDownload,ref:webViewRef,source:sourceResolved},nativeConfig==null?void 0:nativeConfig.props),"webViewKey");return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:webViewContainerStyle,children:[webView,otherView]});});var isFileUploadSupported=function(){var _ref6=(0,_asyncToGenerator2.default)(function*(){return true;});return function isFileUploadSupported(){return _ref6.apply(this,arguments);};}();var WebView=Object.assign(WebViewComponent,{isFileUploadSupported:isFileUploadSupported});var _default=exports.default=WebView; \ No newline at end of file +var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");Object.defineProperty(exports,"__esModule",{value:true});exports.default=void 0;var _asyncToGenerator2=_interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));var _defineProperty2=_interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));var _slicedToArray2=_interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));var _objectWithoutProperties2=_interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));var _react=_interopRequireWildcard(require("react"));var _reactNative=require("react-native");var _invariant=_interopRequireDefault(require("invariant"));var _RNCWebViewNativeComponent=_interopRequireWildcard(require("./RNCWebViewNativeComponent"));var _NativeRNCWebViewModule=_interopRequireDefault(require("./NativeRNCWebViewModule"));var _WebViewShared=require("./WebViewShared");var _WebView=_interopRequireDefault(require("./WebView.styles"));var _jsxRuntime=require("react/jsx-runtime");var _excluded=["fraudulentWebsiteWarningEnabled","javaScriptEnabled","cacheEnabled","originWhitelist","useSharedProcessPool","textInteractionEnabled","injectedJavaScript","injectedJavaScriptBeforeContentLoaded","injectedJavaScriptForMainFrameOnly","injectedJavaScriptBeforeContentLoadedForMainFrameOnly","injectedJavaScriptObject","startInLoadingState","onNavigationStateChange","onLoadStart","onError","onLoad","onLoadEnd","onLoadProgress","onContentProcessDidTerminate","onFileDownload","onHttpError","onMessage","onOpenWindow","renderLoading","renderError","style","containerStyle","source","nativeConfig","allowsInlineMediaPlayback","allowsPictureInPictureMediaPlayback","allowsAirPlayForMediaPlayback","mediaPlaybackRequiresUserAction","dataDetectorTypes","incognito","decelerationRate","onShouldStartLoadWithRequest","pageCurlTuning"];var _this=this,_jsxFileName="/Users/artemlitch/readwise/react-native-webview/src/WebView.ios.tsx";function _interopRequireWildcard(e,t){if("function"==typeof WeakMap)var r=new WeakMap(),n=new WeakMap();return(_interopRequireWildcard=function _interopRequireWildcard(e,t){if(!t&&e&&e.__esModule)return e;var o,i,f={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return f;if(o=t?n:r){if(o.has(e))return o.get(e);o.set(e,f);}for(var _t in e)"default"!==_t&&{}.hasOwnProperty.call(e,_t)&&((i=(o=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,_t))&&(i.get||i.set)?o(f,_t,i):f[_t]=e[_t]);return f;})(e,t);}var resolveAssetSource=_reactNative.Image.resolveAssetSource;var processDecelerationRate=function processDecelerationRate(decelerationRate){var newDecelerationRate=decelerationRate;if(newDecelerationRate==='normal'){newDecelerationRate=0.998;}else if(newDecelerationRate==='fast'){newDecelerationRate=0.99;}return newDecelerationRate;};var useWarnIfChanges=function useWarnIfChanges(value,name){var ref=(0,_react.useRef)(value);if(ref.current!==value){console.warn(`Changes to property ${name} do nothing after the initial render.`);ref.current=value;}};var WebViewComponent=(0,_react.forwardRef)(function(_ref,ref){var _ref$fraudulentWebsit=_ref.fraudulentWebsiteWarningEnabled,fraudulentWebsiteWarningEnabled=_ref$fraudulentWebsit===void 0?true:_ref$fraudulentWebsit,_ref$javaScriptEnable=_ref.javaScriptEnabled,javaScriptEnabled=_ref$javaScriptEnable===void 0?true:_ref$javaScriptEnable,_ref$cacheEnabled=_ref.cacheEnabled,cacheEnabled=_ref$cacheEnabled===void 0?true:_ref$cacheEnabled,_ref$originWhitelist=_ref.originWhitelist,originWhitelist=_ref$originWhitelist===void 0?_WebViewShared.defaultOriginWhitelist:_ref$originWhitelist,_ref$useSharedProcess=_ref.useSharedProcessPool,useSharedProcessPool=_ref$useSharedProcess===void 0?true:_ref$useSharedProcess,_ref$textInteractionE=_ref.textInteractionEnabled,textInteractionEnabled=_ref$textInteractionE===void 0?true:_ref$textInteractionE,injectedJavaScript=_ref.injectedJavaScript,injectedJavaScriptBeforeContentLoaded=_ref.injectedJavaScriptBeforeContentLoaded,_ref$injectedJavaScri=_ref.injectedJavaScriptForMainFrameOnly,injectedJavaScriptForMainFrameOnly=_ref$injectedJavaScri===void 0?true:_ref$injectedJavaScri,_ref$injectedJavaScri2=_ref.injectedJavaScriptBeforeContentLoadedForMainFrameOnly,injectedJavaScriptBeforeContentLoadedForMainFrameOnly=_ref$injectedJavaScri2===void 0?true:_ref$injectedJavaScri2,injectedJavaScriptObject=_ref.injectedJavaScriptObject,startInLoadingState=_ref.startInLoadingState,onNavigationStateChange=_ref.onNavigationStateChange,onLoadStart=_ref.onLoadStart,onError=_ref.onError,onLoad=_ref.onLoad,onLoadEnd=_ref.onLoadEnd,onLoadProgress=_ref.onLoadProgress,onContentProcessDidTerminateProp=_ref.onContentProcessDidTerminate,onFileDownload=_ref.onFileDownload,onHttpErrorProp=_ref.onHttpError,onMessageProp=_ref.onMessage,onOpenWindowProp=_ref.onOpenWindow,renderLoading=_ref.renderLoading,renderError=_ref.renderError,style=_ref.style,containerStyle=_ref.containerStyle,source=_ref.source,nativeConfig=_ref.nativeConfig,allowsInlineMediaPlayback=_ref.allowsInlineMediaPlayback,_ref$allowsPictureInP=_ref.allowsPictureInPictureMediaPlayback,allowsPictureInPictureMediaPlayback=_ref$allowsPictureInP===void 0?true:_ref$allowsPictureInP,allowsAirPlayForMediaPlayback=_ref.allowsAirPlayForMediaPlayback,mediaPlaybackRequiresUserAction=_ref.mediaPlaybackRequiresUserAction,dataDetectorTypes=_ref.dataDetectorTypes,incognito=_ref.incognito,decelerationRateProp=_ref.decelerationRate,onShouldStartLoadWithRequestProp=_ref.onShouldStartLoadWithRequest,pageCurlTuning=_ref.pageCurlTuning,otherProps=(0,_objectWithoutProperties2.default)(_ref,_excluded);var webViewRef=(0,_react.useRef)(null);var onShouldStartLoadWithRequestCallback=(0,_react.useCallback)(function(shouldStart,_url){var lockIdentifier=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0;_NativeRNCWebViewModule.default.shouldStartLoadWithLockIdentifier(shouldStart,lockIdentifier);},[]);var _useWebViewLogic=(0,_WebViewShared.useWebViewLogic)({onNavigationStateChange:onNavigationStateChange,onLoad:onLoad,onError:onError,onHttpErrorProp:onHttpErrorProp,onLoadEnd:onLoadEnd,onLoadProgress:onLoadProgress,onLoadStart:onLoadStart,onMessageProp:onMessageProp,onOpenWindowProp:onOpenWindowProp,startInLoadingState:startInLoadingState,originWhitelist:originWhitelist,onShouldStartLoadWithRequestProp:onShouldStartLoadWithRequestProp,onShouldStartLoadWithRequestCallback:onShouldStartLoadWithRequestCallback,onContentProcessDidTerminateProp:onContentProcessDidTerminateProp}),onLoadingStart=_useWebViewLogic.onLoadingStart,onShouldStartLoadWithRequest=_useWebViewLogic.onShouldStartLoadWithRequest,onMessage=_useWebViewLogic.onMessage,viewState=_useWebViewLogic.viewState,setViewState=_useWebViewLogic.setViewState,lastErrorEvent=_useWebViewLogic.lastErrorEvent,onHttpError=_useWebViewLogic.onHttpError,onLoadingError=_useWebViewLogic.onLoadingError,onLoadingFinish=_useWebViewLogic.onLoadingFinish,onLoadingProgress=_useWebViewLogic.onLoadingProgress,onOpenWindow=_useWebViewLogic.onOpenWindow,onContentProcessDidTerminate=_useWebViewLogic.onContentProcessDidTerminate;(0,_react.useImperativeHandle)(ref,function(){return{goForward:function goForward(){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.goForward(webViewRef.current);},goBack:function goBack(){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.goBack(webViewRef.current);},reload:function reload(){setViewState('LOADING');if(webViewRef.current){_RNCWebViewNativeComponent.Commands.reload(webViewRef.current);}},stopLoading:function stopLoading(){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.stopLoading(webViewRef.current);},postMessage:function postMessage(data){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.postMessage(webViewRef.current,data);},injectJavaScript:function injectJavaScript(data){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.injectJavaScript(webViewRef.current,data);},callAsyncJavaScript:function callAsyncJavaScript(functionBody,argument){var tag=webViewRef.current&&(0,_reactNative.findNodeHandle)(webViewRef.current);if(tag==null){return Promise.reject(new Error('webview is not mounted'));}return _NativeRNCWebViewModule.default.callAsyncJavaScript(tag,functionBody,argument);},requestFocus:function requestFocus(){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.requestFocus(webViewRef.current);},clearCache:function clearCache(includeDiskFiles){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.clearCache(webViewRef.current,includeDiskFiles);},setTintColor:function setTintColor(red,green,blue,alpha){return webViewRef.current&&_RNCWebViewNativeComponent.Commands.setTintColor(webViewRef.current,red,green,blue,alpha);}};},[setViewState,webViewRef]);useWarnIfChanges(allowsInlineMediaPlayback,'allowsInlineMediaPlayback');useWarnIfChanges(allowsPictureInPictureMediaPlayback,'allowsPictureInPictureMediaPlayback');useWarnIfChanges(allowsAirPlayForMediaPlayback,'allowsAirPlayForMediaPlayback');useWarnIfChanges(incognito,'incognito');useWarnIfChanges(mediaPlaybackRequiresUserAction,'mediaPlaybackRequiresUserAction');useWarnIfChanges(dataDetectorTypes,'dataDetectorTypes');var otherView=null;if(viewState==='LOADING'){otherView=(renderLoading||_WebViewShared.defaultRenderLoading)();}else if(viewState==='ERROR'){var _lastErrorEvent$code,_lastErrorEvent$descr;(0,_invariant.default)(lastErrorEvent!=null,'lastErrorEvent expected to be non-null');otherView=(renderError||_WebViewShared.defaultRenderError)(lastErrorEvent==null?void 0:lastErrorEvent.domain,(_lastErrorEvent$code=lastErrorEvent==null?void 0:lastErrorEvent.code)!=null?_lastErrorEvent$code:0,(_lastErrorEvent$descr=lastErrorEvent==null?void 0:lastErrorEvent.description)!=null?_lastErrorEvent$descr:'');}else if(viewState!=='IDLE'){console.error(`RNCWebView invalid state encountered: ${viewState}`);}var webViewStyles=[_WebView.default.container,_WebView.default.webView,style];var webViewContainerStyle=[_WebView.default.container,containerStyle];var decelerationRate=processDecelerationRate(decelerationRateProp);var NativeWebView=(nativeConfig==null?void 0:nativeConfig.component)||_RNCWebViewNativeComponent.default;var sourceResolved=resolveAssetSource(source);var newSource=typeof sourceResolved==='object'?Object.entries(sourceResolved).reduce(function(prev,_ref2){var _ref3=(0,_slicedToArray2.default)(_ref2,2),currKey=_ref3[0],currValue=_ref3[1];return Object.assign({},prev,(0,_defineProperty2.default)({},currKey,currKey==='headers'&&currValue&&typeof currValue==='object'?Object.entries(currValue).map(function(_ref4){var _ref5=(0,_slicedToArray2.default)(_ref4,2),key=_ref5[0],value=_ref5[1];return{name:key,value:value};}):currValue));},{}):sourceResolved;var webView=(0,_jsxRuntime.jsx)(NativeWebView,Object.assign({},otherProps,{fraudulentWebsiteWarningEnabled:fraudulentWebsiteWarningEnabled,javaScriptEnabled:javaScriptEnabled,cacheEnabled:cacheEnabled,useSharedProcessPool:useSharedProcessPool,textInteractionEnabled:textInteractionEnabled,decelerationRate:decelerationRate,messagingEnabled:typeof onMessageProp==='function',messagingModuleName:"",onLoadingError:onLoadingError,onLoadingFinish:onLoadingFinish,onLoadingProgress:onLoadingProgress,onFileDownload:onFileDownload,onLoadingStart:onLoadingStart,onHttpError:onHttpError,onMessage:onMessage,onOpenWindow:onOpenWindowProp&&onOpenWindow,hasOnOpenWindowEvent:onOpenWindowProp!==undefined,onShouldStartLoadWithRequest:onShouldStartLoadWithRequest,pageCurlTuning:pageCurlTuning&&JSON.stringify(pageCurlTuning),onContentProcessDidTerminate:onContentProcessDidTerminate,injectedJavaScript:injectedJavaScript,injectedJavaScriptBeforeContentLoaded:injectedJavaScriptBeforeContentLoaded,injectedJavaScriptForMainFrameOnly:injectedJavaScriptForMainFrameOnly,injectedJavaScriptBeforeContentLoadedForMainFrameOnly:injectedJavaScriptBeforeContentLoadedForMainFrameOnly,injectedJavaScriptObject:JSON.stringify(injectedJavaScriptObject),dataDetectorTypes:!dataDetectorTypes||Array.isArray(dataDetectorTypes)?dataDetectorTypes:[dataDetectorTypes],allowsAirPlayForMediaPlayback:allowsAirPlayForMediaPlayback,allowsInlineMediaPlayback:allowsInlineMediaPlayback,allowsPictureInPictureMediaPlayback:allowsPictureInPictureMediaPlayback,incognito:incognito,mediaPlaybackRequiresUserAction:mediaPlaybackRequiresUserAction,newSource:newSource,style:webViewStyles,hasOnFileDownload:!!onFileDownload,ref:webViewRef,source:sourceResolved},nativeConfig==null?void 0:nativeConfig.props),"webViewKey");return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:webViewContainerStyle,children:[webView,otherView]});});var isFileUploadSupported=function(){var _ref6=(0,_asyncToGenerator2.default)(function*(){return true;});return function isFileUploadSupported(){return _ref6.apply(this,arguments);};}();var WebView=Object.assign(WebViewComponent,{isFileUploadSupported:isFileUploadSupported});var _default=exports.default=WebView; \ No newline at end of file diff --git a/src/NativeRNCWebViewModule.ts b/src/NativeRNCWebViewModule.ts index 220d2390fe..ba2c8f2725 100644 --- a/src/NativeRNCWebViewModule.ts +++ b/src/NativeRNCWebViewModule.ts @@ -1,6 +1,14 @@ import type { TurboModule } from 'react-native'; import { TurboModuleRegistry } from 'react-native'; -import { Double } from 'react-native/Libraries/Types/CodegenTypes'; +import type { + Double, + EventEmitter, +} from 'react-native/Libraries/Types/CodegenTypes'; + +export type FastMessageEvent = { + viewTag: Double; + data: string; +}; export interface Spec extends TurboModule { isFileUploadSupported(): Promise; @@ -8,6 +16,12 @@ export interface Spec extends TurboModule { shouldStart: boolean, lockIdentifier: Double ): void; + callAsyncJavaScript( + viewTag: Double, + functionBody: string, + argument: string + ): Promise; + readonly onFastMessage: EventEmitter; } export default TurboModuleRegistry.getEnforcing('RNCWebViewModule'); diff --git a/src/WebView.ios.tsx b/src/WebView.ios.tsx index 27b398e856..aef027464f 100644 --- a/src/WebView.ios.tsx +++ b/src/WebView.ios.tsx @@ -4,7 +4,13 @@ import React, { useImperativeHandle, useRef, } from 'react'; -import { Image, View, ImageSourcePropType, HostComponent } from 'react-native'; +import { + Image, + View, + ImageSourcePropType, + HostComponent, + findNodeHandle, +} from 'react-native'; import invariant from 'invariant'; import RNCWebView, { Commands, NativeProps } from './RNCWebViewNativeComponent'; @@ -155,6 +161,13 @@ const WebViewComponent = forwardRef<{}, IOSWebViewProps>( injectJavaScript: (data: string) => webViewRef.current && Commands.injectJavaScript(webViewRef.current, data), + callAsyncJavaScript: (functionBody: string, argument: string) => { + const tag = webViewRef.current && findNodeHandle(webViewRef.current); + if (tag == null) { + return Promise.reject(new Error('webview is not mounted')); + } + return RNCWebViewModule.callAsyncJavaScript(tag, functionBody, argument); + }, requestFocus: () => webViewRef.current && Commands.requestFocus(webViewRef.current), clearCache: (includeDiskFiles: boolean) => From cdf7be249435acd96f152cdcc0e173543a0426c3 Mon Sep 17 00:00:00 2001 From: Artem Litchmanov Date: Sat, 17 Oct 2026 03:32:34 -0700 Subject: [PATCH 2/2] Drop the fast message handler; type callAsyncJavaScript It measured slower than onMessage, so only the callAsyncJavaScript call path remains. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014HGrJetPUopRfPnfocezrH --- apple/RNCWebViewImpl.h | 2 -- apple/RNCWebViewImpl.m | 15 --------------- apple/RNCWebViewModule.h | 6 +++--- apple/RNCWebViewModule.mm | 9 --------- index.d.ts | 5 +++++ lib/NativeRNCWebViewModule.d.ts | 1 + src/NativeRNCWebViewModule.ts | 11 +---------- 7 files changed, 10 insertions(+), 39 deletions(-) diff --git a/apple/RNCWebViewImpl.h b/apple/RNCWebViewImpl.h index 9d8787fa67..6fb251871d 100644 --- a/apple/RNCWebViewImpl.h +++ b/apple/RNCWebViewImpl.h @@ -170,8 +170,6 @@ shouldStartLoadForRequest:(NSMutableDictionary *)request completion:(void (^_Nonnull)(id _Nullable result, NSError *_Nullable error))completion; + (nullable RNCWebViewImpl *)implForReactTag:(NSInteger)reactTag; + (void)registerImpl:(nullable RNCWebViewImpl *)impl forReactTag:(NSInteger)reactTag; -+ (void)setFastMessageSink:(nullable void (^)(NSInteger reactTag, NSString *_Nonnull data))sink; -@property (nonatomic, assign) NSInteger fastMessageReactTag; - (void)goForward; - (void)goBack; - (void)reload; diff --git a/apple/RNCWebViewImpl.m b/apple/RNCWebViewImpl.m index df35ed1f36..9fdc415e79 100644 --- a/apple/RNCWebViewImpl.m +++ b/apple/RNCWebViewImpl.m @@ -26,8 +26,6 @@ static NSString *const MessageHandlerName = @"ReactNativeWebView"; static NSString *const PageSpacersMessageHandlerName = @"pageSpacers"; static NSString *const PageCurlMessageHandlerName = @"pageCurl"; -static NSString *const FastMessageHandlerName = @"ReactNativeWebViewFast"; -static void (^RNCWebViewFastMessageSink)(NSInteger reactTag, NSString *data); static NSURLCredential* clientAuthenticationCredential; static NSDictionary* customCertificatesForHost; @@ -506,8 +504,6 @@ - (WKWebViewConfiguration *)setUpWkWebViewConfig // Shim the HTML5 history API: [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self] name:HistoryShimName]; - [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self] - name:FastMessageHandlerName]; [self resetupScripts:wkWebViewConfig]; if(@available(macos 10.11, ios 9.0, *)) { @@ -647,7 +643,6 @@ - (void)removeFromSuperview { if (_webView) { [_webView.configuration.userContentController removeScriptMessageHandlerForName:HistoryShimName]; - [_webView.configuration.userContentController removeScriptMessageHandlerForName:FastMessageHandlerName]; [_webView.configuration.userContentController removeScriptMessageHandlerForName:MessageHandlerName]; [_webView removeObserver:self forKeyPath:@"estimatedProgress"]; [_webView removeFromSuperview]; @@ -930,10 +925,6 @@ - (void)userContentController:(WKUserContentController *)userContentController [event addEntriesFromDictionary: @{@"navigationType": message.body}]; _onLoadingFinish(event); } - } else if ([message.name isEqualToString:FastMessageHandlerName]) { - if (RNCWebViewFastMessageSink && _fastMessageReactTag != 0 && [message.body isKindOfClass:[NSString class]]) { - RNCWebViewFastMessageSink(_fastMessageReactTag, message.body); - } } else if ([message.name isEqualToString:MessageHandlerName]) { if (_onMessage) { NSMutableDictionary *event = [self baseEvent]; @@ -1954,15 +1945,9 @@ + (RNCWebViewImpl *)implForReactTag:(NSInteger)reactTag return [RNCWebViewImplRegistry() objectForKey:@(reactTag)]; } -+ (void)setFastMessageSink:(void (^)(NSInteger reactTag, NSString *data))sink -{ - RNCWebViewFastMessageSink = [sink copy]; -} - + (void)registerImpl:(RNCWebViewImpl *)impl forReactTag:(NSInteger)reactTag { if (impl) { - impl.fastMessageReactTag = reactTag; [RNCWebViewImplRegistry() setObject:impl forKey:@(reactTag)]; } else { [RNCWebViewImplRegistry() removeObjectForKey:@(reactTag)]; diff --git a/apple/RNCWebViewModule.h b/apple/RNCWebViewModule.h index d58e63d694..586020930e 100644 --- a/apple/RNCWebViewModule.h +++ b/apple/RNCWebViewModule.h @@ -9,13 +9,13 @@ NS_ASSUME_NONNULL_BEGIN +@interface RNCWebViewModule : NSObject < #ifdef RCT_NEW_ARCH_ENABLED -@interface RNCWebViewModule : NativeRNCWebViewModuleSpecBase < NativeRNCWebViewModuleSpec -> #else -@interface RNCWebViewModule : NSObject +RCTBridgeModule #endif /* RCT_NEW_ARCH_ENABLED */ +> @end NS_ASSUME_NONNULL_END diff --git a/apple/RNCWebViewModule.mm b/apple/RNCWebViewModule.mm index be8bd89925..a75af04d42 100644 --- a/apple/RNCWebViewModule.mm +++ b/apple/RNCWebViewModule.mm @@ -48,15 +48,6 @@ - (dispatch_queue_t)methodQueue } #ifdef RCT_NEW_ARCH_ENABLED -- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper -{ - [super setEventEmitterCallback:eventEmitterCallbackWrapper]; - __weak RNCWebViewModule *weakSelf = self; - [RNCWebViewImpl setFastMessageSink:^(NSInteger reactTag, NSString *data) { - [weakSelf emitOnFastMessage:@{@"viewTag": @(reactTag), @"data": data}]; - }]; -} - - (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { return std::make_shared(params); } diff --git a/index.d.ts b/index.d.ts index a112f17e10..8d4c86609c 100644 --- a/index.d.ts +++ b/index.d.ts @@ -32,6 +32,11 @@ declare class WebView

extends Component { */ injectJavaScript: (script: string) => void; + /** + * iOS only. Runs `functionBody` in the page with `argument` in scope and resolves with the string it returns. + */ + callAsyncJavaScript?: (functionBody: string, argument: string) => Promise; + /** * Focuses on WebView rendered page. */ diff --git a/lib/NativeRNCWebViewModule.d.ts b/lib/NativeRNCWebViewModule.d.ts index 0174988bb7..4922bf039b 100644 --- a/lib/NativeRNCWebViewModule.d.ts +++ b/lib/NativeRNCWebViewModule.d.ts @@ -3,6 +3,7 @@ import { Double } from 'react-native/Libraries/Types/CodegenTypes'; export interface Spec extends TurboModule { isFileUploadSupported(): Promise; shouldStartLoadWithLockIdentifier(shouldStart: boolean, lockIdentifier: Double): void; + callAsyncJavaScript(viewTag: Double, functionBody: string, argument: string): Promise; } declare const _default: Spec; export default _default; diff --git a/src/NativeRNCWebViewModule.ts b/src/NativeRNCWebViewModule.ts index ba2c8f2725..19403f214b 100644 --- a/src/NativeRNCWebViewModule.ts +++ b/src/NativeRNCWebViewModule.ts @@ -1,14 +1,6 @@ import type { TurboModule } from 'react-native'; import { TurboModuleRegistry } from 'react-native'; -import type { - Double, - EventEmitter, -} from 'react-native/Libraries/Types/CodegenTypes'; - -export type FastMessageEvent = { - viewTag: Double; - data: string; -}; +import { Double } from 'react-native/Libraries/Types/CodegenTypes'; export interface Spec extends TurboModule { isFileUploadSupported(): Promise; @@ -21,7 +13,6 @@ export interface Spec extends TurboModule { functionBody: string, argument: string ): Promise; - readonly onFastMessage: EventEmitter; } export default TurboModuleRegistry.getEnforcing('RNCWebViewModule');