Skip to content

Commit

Permalink
[iOS] support releasing native WebView without a ref to the React Web…
Browse files Browse the repository at this point in the history
…View (#4)
  • Loading branch information
donaldchen committed May 6, 2022
1 parent 0356b63 commit 03d597c
Show file tree
Hide file tree
Showing 17 changed files with 97 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ public void onShouldStartLoadWithRequestCallback(final boolean shouldStart, fina
}
}

@ReactMethod
public void releaseWebView(final String webViewKey) {
// no-op for now
}

public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {

if (filePathCallback == null && filePathCallbackLegacy == null) {
Expand Down
1 change: 1 addition & 0 deletions apple/RNCWebView.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ typedef enum RNCWebViewPermissionGrantType : NSUInteger {
#if !TARGET_OS_OSX
- (void)addPullToRefreshControl;
- (void)pullToRefresh:(UIRefreshControl *_Nonnull)refreshControl;
- (void)cleanUpWebView;
#endif

@end
29 changes: 17 additions & 12 deletions apple/RNCWebViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#import <React/RCTUIManager.h>
#import <React/RCTDefines.h>
#import "RNCWebView.h"
#import "RNCWKWebViewMapManager.h"
#import "RNCWebViewMapManager.h"

@interface RNCWebViewManager () <RNCWebViewDelegate>
@end
Expand Down Expand Up @@ -245,18 +247,6 @@ - (RCTUIView *)view
}];
}

RCT_EXPORT_METHOD(release:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNCWebView *> *viewRegistry) {
RNCWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RNCWebView class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RNCWebView, got: %@", view);
} else {
[view releaseWebView];
}
}];
}


#pragma mark - Exported synchronous methods

Expand Down Expand Up @@ -292,4 +282,19 @@ - (BOOL) webView:(RNCWebView *)webView
}
}

RCT_EXPORT_METHOD(releaseWebView:(nonnull NSString *)webViewKey)
{
NSMutableDictionary *sharedWKWebViewDictionary = [[RNCWKWebViewMapManager sharedManager] sharedWKWebViewDictionary];
NSMutableDictionary *sharedRNCWebViewDictionary= [[RNCWebViewMapManager sharedManager] sharedRNCWebViewDictionary];

sharedWKWebViewDictionary[webViewKey] = nil;

RNCWebView *rncWebView = sharedRNCWebViewDictionary[webViewKey];

if (rncWebView != nil) {
[rncWebView cleanUpWebView];
sharedRNCWebViewDictionary[webViewKey] = nil;
}
}

@end
2 changes: 1 addition & 1 deletion example/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export default class App extends Component<Props, State> {
onPress={() => this._changeTest('ApplePay')}
/>
)}
{Platform.OS === 'ios' && (
{(
<Button
testID="testType_portals"
title="Portals"
Expand Down
51 changes: 44 additions & 7 deletions example/examples/Portals.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import * as React from 'react';
import {View, Button} from 'react-native';
import WebView, {releaseWebView} from 'react-native-webview';
import PortalGate from '../portals/PortalGate';
import PortalProvider from '../portals/PortalProvider';

import WebView from 'react-native-webview';
import { PortalContext } from '../portals/PortalContext';

const IFRAME_URI = 'https://www.usaswimming.org';
const BLUE_GATE_NAME = 'blueGate';
const GREEN_GATE_NAME = 'greenGate';
const WEB_VIEW_KEY = 'PortalTestingWebViewKey'
const IFRAME_WIDTH = 360;
const PORTALS_PAGE = 0;
const NONPORTALS_PAGE = 1;

const source = {
html: `
Expand All @@ -29,14 +30,33 @@ const source = {
};

export default function Portals() {
const [pageNumber, setPageNumber] = React.useState(PORTALS_PAGE);

const togglePages = () => {
const nextPage = pageNumber === PORTALS_PAGE ? NONPORTALS_PAGE : PORTALS_PAGE;
setPageNumber(nextPage);
}

return (
<PortalProvider>
<PortalGates/>
</PortalProvider>
<>
<Button
title="Toggle Pages"
onPress={togglePages}
/>
{pageNumber === PORTALS_PAGE ? (
<PortalProvider>
<PortalGatesPage/>
</PortalProvider>
) : null }
{pageNumber === NONPORTALS_PAGE ? (
<NonPortalsPage/>
) : null }
</>

)
}

function PortalGates() {
function PortalGatesPage() {
const {gates, teleport} = React.useContext(PortalContext);

const [releaseCounter, setReleaseCounter] = React.useState(0);
Expand Down Expand Up @@ -68,7 +88,11 @@ function PortalGates() {
teleport(gates, GREEN_GATE_NAME, undefined);
teleport(gates, BLUE_GATE_NAME, undefined);
setReleaseCounter(releaseCounter + 1);
webViewRef.current?.release();
releaseWebView(WEB_VIEW_KEY);
}, []);

React.useEffect(() => {
teleportToBlueGate();
}, []);

return (
Expand All @@ -94,3 +118,16 @@ function PortalGates() {
</>
)
}

function NonPortalsPage() {
const release = () => {
releaseWebView(WEB_VIEW_KEY);
};

return (
<Button
title="Release WebView"
onPress={release}
/>
);
}
2 changes: 1 addition & 1 deletion example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ SPEC CHECKSUMS:
React-RCTVibration: e3ffca672dd3772536cb844274094b0e2c31b187
React-runtimeexecutor: dec32ee6f2e2a26e13e58152271535fadff5455a
ReactCommon: 57b69f6383eafcbd7da625bfa6003810332313c4
ReactTestApp-DevSupport: c66bec93814c2b06dd8ca505243289d82cef996a
ReactTestApp-DevSupport: b581ac76d7b5dcf7c22f213e8012596199504f2f
ReactTestApp-Resources: 74a1cf509f4e7962b16361ea4e73cba3648fff5d
SwiftLint: 6bc52a21f0fd44cab9aa2dc8e534fb9f5e3ec507
Yoga: e7dc4e71caba6472ff48ad7d234389b91dadc280
Expand Down
12 changes: 4 additions & 8 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Component } from 'react';
// eslint-disable-next-line

import releaseWebView from './lib/releaseWebView';

import { IOSWebViewProps, AndroidWebViewProps, WindowsWebViewProps } from './lib/WebViewTypes';

export { FileDownload, WebViewMessageEvent, WebViewNavigation } from "./lib/WebViewTypes";
Expand Down Expand Up @@ -59,14 +62,7 @@ declare class WebView<P = {}> extends Component<WebViewProps & P> {
* Tells this WebView to clear its internal back/forward list.
*/
clearHistory?: () => void;

/**
* (iOS only)
* Explicitly release the native WebView instance if it hasn't released after the React
* component unmounts;
*/
release: () => void;
}

export {WebView};
export {WebView, releaseWebView};
export default WebView;
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import WebView from './lib/WebView';
import releaseWebView from './lib/releaseWebView';

export { WebView };
export { WebView, releaseWebView };
export default WebView;
8 changes: 0 additions & 8 deletions src/WebView.android.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,6 @@ class WebView extends React.Component<AndroidWebViewProps, State> {
);
};

release = () => {
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
this.getCommands().release,
undefined
);
};

/**
* We return an event with a bunch of fields including:
* url, title, loading, canGoBack, canGoForward
Expand Down
8 changes: 0 additions & 8 deletions src/WebView.ios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,6 @@ class WebView extends React.Component<IOSWebViewProps, State> {
);
};

release = () => {
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
this.getCommands().release,
undefined
);
}

/**
* We return an event with a bunch of fields including:
* url, title, loading, canGoBack, canGoForward
Expand Down
2 changes: 1 addition & 1 deletion src/WebViewTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
NativeScrollEvent,
} from 'react-native';

type WebViewCommands = 'goForward' | 'goBack' | 'reload' | 'stopLoading' | 'postMessage' | 'injectJavaScript' | 'loadUrl' | 'requestFocus' | 'release';
type WebViewCommands = 'goForward' | 'goBack' | 'reload' | 'stopLoading' | 'postMessage' | 'injectJavaScript' | 'loadUrl' | 'requestFocus';

type AndroidWebViewCommands = 'clearHistory' | 'clearCache' | 'clearFormData';

Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import WebView from './WebView';
import releaseWebView from './releaseWebView';

export { WebView };
export { WebView, releaseWebView };
export default WebView;
5 changes: 5 additions & 0 deletions src/releaseWebView.android.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { NativeModules } from 'react-native';

export default function releaseWebView(webViewKey: string) {
NativeModules.RNCWebView.releaseWebView(webViewKey);
}
5 changes: 5 additions & 0 deletions src/releaseWebView.ios.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { NativeModules } from 'react-native';

export default function releaseWebView(webViewKey: string) {
NativeModules.RNCWebViewManager.releaseWebView(webViewKey);
}
3 changes: 3 additions & 0 deletions src/releaseWebView.macos.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function releaseWebView(_webViewKey: string) {
// no-op
}
3 changes: 3 additions & 0 deletions src/releaseWebView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function releaseWebView(_webViewKey: string) {
// no-op. This should get overwritten by platform-specific implementations
}
3 changes: 3 additions & 0 deletions src/releaseWebView.windows.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function releaseWebView(_webViewKey: string) {
// no-op
}

0 comments on commit 03d597c

Please sign in to comment.