Skip to content

PostMessage Bridge activation inconsistent btw platforms #13006

@dotnetprofessional

Description

@dotnetprofessional

Description

The Android and iOS platforms differ in the order in which they setup the linkBridge. The iOS version sets the bridge up first then executes user supplied scripts. However, Android does this in the reverse order. The iOS order is preferred and considered to be the correct order. This has caused an issue with our application in that we want to override the postMessage of the bridge, but out code executes prior to the bridge being configured, which means we have to resort to a hack of setting a setTimeout. This is not necessary with the iOS version.

Android implementation:

    public void onPageFinished(WebView webView, String url) {
      super.onPageFinished(webView, url);

      if (!mLastLoadFailed) {
        ReactWebView reactWebView = (ReactWebView) webView;
        reactWebView.callInjectedJavaScript();
        reactWebView.linkBridge();
        emitFinishEvent(webView, url);
      }
    }

iOS

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
  if (_messagingEnabled) {
    #if RCT_DEV
    // See isNative in lodash
    NSString *testPostMessageNative = @"String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')";
    BOOL postMessageIsNative = [
      [webView stringByEvaluatingJavaScriptFromString:testPostMessageNative]
      isEqualToString:@"true"
    ];
    if (!postMessageIsNative) {
      RCTLogError(@"Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined");
    }
    #endif
    NSString *source = [NSString stringWithFormat:
      @"window.originalPostMessage = window.postMessage;"
      "window.postMessage = function(data) {"
        "window.location = '%@://%@?' + encodeURIComponent(String(data));"
      "};", RCTJSNavigationScheme, RCTJSPostMessageHost
    ];
    [webView stringByEvaluatingJavaScriptFromString:source];
  }
  if (_injectedJavaScript != nil) {
    NSString *jsEvaluationValue = [webView stringByEvaluatingJavaScriptFromString:_injectedJavaScript];

    NSMutableDictionary<NSString *, id> *event = [self baseEvent];
    event[@"jsEvaluationValue"] = jsEvaluationValue;

    _onLoadingFinish(event);
  }
  // we only need the final 'finishLoad' call so only fire the event when we're actually done loading.
  else if (_onLoadingFinish && !webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) {
    _onLoadingFinish([self baseEvent]);
  }
}

Solution

Swap these two lines in the Android version:

        reactWebView.callInjectedJavaScript();
        reactWebView.linkBridge();

to be this:

        reactWebView.linkBridge();
        reactWebView.callInjectedJavaScript();

Additional Information

  • React Native version: 0.42.0
  • Operating System: Android

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions