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
Original file line number Diff line number Diff line change
Expand Up @@ -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<Uri> filePathCallback, String acceptType) {
mRNCWebViewModuleImpl.startPhotoPickerIntent(acceptType, filePathCallback);
}
Expand Down
11 changes: 11 additions & 0 deletions apple/RNCWebView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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]) {
Expand Down
5 changes: 5 additions & 0 deletions apple/RNCWebViewImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)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)goForward;
- (void)goBack;
- (void)reload;
Expand Down
39 changes: 39 additions & 0 deletions apple/RNCWebViewImpl.m
Original file line number Diff line number Diff line change
Expand Up @@ -1930,6 +1930,45 @@ - (void)injectJavaScript:(NSString *)script
[self evaluateJS: script thenCall: nil];
}

static NSMapTable<NSNumber *, RNCWebViewImpl *> *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)registerImpl:(RNCWebViewImpl *)impl forReactTag:(NSInteger)reactTag
{
if (impl) {
[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];
Expand Down
26 changes: 26 additions & 0 deletions apple/RNCWebViewModule.mm
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#import "RNCWebViewModule.h"

#import "RNCWebViewDecisionManager.h"
#import "RNCWebViewImpl.h"

#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTFabricComponentsPlugins.h>
Expand All @@ -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));
Expand Down
5 changes: 5 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ declare class WebView<P = {}> extends Component<WebViewProps & P> {
*/
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<string | null>;

/**
* Focuses on WebView rendered page.
*/
Expand Down
1 change: 1 addition & 0 deletions lib/NativeRNCWebViewModule.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Double } from 'react-native/Libraries/Types/CodegenTypes';
export interface Spec extends TurboModule {
isFileUploadSupported(): Promise<boolean>;
shouldStartLoadWithLockIdentifier(shouldStart: boolean, lockIdentifier: Double): void;
callAsyncJavaScript(viewTag: Double, functionBody: string, argument: string): Promise<string | null>;
}
declare const _default: Spec;
export default _default;
2 changes: 1 addition & 1 deletion lib/WebView.ios.js

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/NativeRNCWebViewModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ export interface Spec extends TurboModule {
shouldStart: boolean,
lockIdentifier: Double
): void;
callAsyncJavaScript(
viewTag: Double,
functionBody: string,
argument: string
): Promise<string | null>;
}

export default TurboModuleRegistry.getEnforcing<Spec>('RNCWebViewModule');
15 changes: 14 additions & 1 deletion src/WebView.ios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
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';
Expand Down Expand Up @@ -155,6 +161,13 @@
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);

Check failure on line 169 in src/WebView.ios.tsx

View workflow job for this annotation

GitHub Actions / Running tests

Replace `tag,·functionBody,·argument` with `⏎············tag,⏎············functionBody,⏎············argument⏎··········`
},
requestFocus: () =>
webViewRef.current && Commands.requestFocus(webViewRef.current),
clearCache: (includeDiskFiles: boolean) =>
Expand Down
Loading