diff --git a/android/src/main/java/io/ionic/portals/reactnative/PortalView.kt b/android/src/main/java/io/ionic/portals/reactnative/PortalView.kt index 8318297..10ce9e2 100644 --- a/android/src/main/java/io/ionic/portals/reactnative/PortalView.kt +++ b/android/src/main/java/io/ionic/portals/reactnative/PortalView.kt @@ -1,11 +1,17 @@ package io.ionic.portals.reactnative +import android.os.Handler +import android.os.Looper import android.util.Log import android.view.Choreographer import android.view.View import android.view.ViewGroup +import android.webkit.WebView import android.widget.FrameLayout import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.LifecycleOwner import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap @@ -19,7 +25,8 @@ import io.ionic.portals.WebVitals private data class PortalViewState( var fragment: PortalFragment?, var portal: RNPortal?, - var initialContext: HashMap? + var initialContext: HashMap?, + var webContentsDebuggingEnabled: Boolean? ) internal class PortalViewManager(private val context: ReactApplicationContext) : @@ -35,13 +42,19 @@ internal class PortalViewManager(private val context: ReactApplicationContext) : // Casting is safe and keeps old versions compatible val initialContext = portal.getMap("initialContext")?.toHashMap() as HashMap? - when (fragmentMap[viewGroup.id]) { - null -> fragmentMap[viewGroup.id] = PortalViewState( - fragment = null, - portal = RNPortalManager.createPortal(portal), - initialContext - ) + val state = fragmentMap.getOrPut(viewGroup.id) { + PortalViewState(null, null, null, null) } + state.portal = RNPortalManager.createPortal(portal) + state.initialContext = initialContext + } + + @ReactProp(name = "webContentsDebuggingEnabled") + fun setWebContentsDebuggingEnabled(viewGroup: ViewGroup, webContentsDebuggingEnabled: Boolean) { + val state = fragmentMap.getOrPut(viewGroup.id) { + PortalViewState(null, null, null, null) + } + state.webContentsDebuggingEnabled = webContentsDebuggingEnabled } override fun getName() = "AndroidPortalView" @@ -99,12 +112,26 @@ internal class PortalViewManager(private val context: ReactApplicationContext) : } val portalFragment = PortalFragment(portal) - viewState.initialContext?.let(portalFragment::setInitialContext) viewState.fragment = portalFragment - val fragmentActivity = context.currentActivity as? FragmentActivity ?: return - fragmentActivity.supportFragmentManager + portalFragment.lifecycle.addObserver(object : LifecycleEventObserver { + override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { + if (event == Lifecycle.Event.ON_RESUME) { + source.lifecycle.removeObserver(this) + + viewState.webContentsDebuggingEnabled?.let { enabled -> + // Post to the next main loop iteration to avoid racing with WebView initialization + Handler(Looper.getMainLooper()).post { + WebView.setWebContentsDebuggingEnabled(enabled) + } + } + } + } + }) + + val activity = context.currentActivity as? FragmentActivity ?: return + activity.supportFragmentManager .beginTransaction() .replace(viewId, portalFragment, "$viewId") .commit() diff --git a/ios/PortalView.mm b/ios/PortalView.mm index 6bf00c5..92108c3 100644 --- a/ios/PortalView.mm +++ b/ios/PortalView.mm @@ -10,4 +10,5 @@ @interface RCT_EXTERN_MODULE(IONPortalViewManager, RCTViewManager) RCT_EXPORT_VIEW_PROPERTY(portal, NSDictionary) +RCT_EXPORT_VIEW_PROPERTY(webContentsDebuggingEnabled, BOOL) @end diff --git a/ios/PortalView.swift b/ios/PortalView.swift index 3293077..7f81ed8 100644 --- a/ios/PortalView.swift +++ b/ios/PortalView.swift @@ -61,7 +61,7 @@ class PortalView: UIView { DispatchQueue.main.async { [weak self] in guard let self = self else { return } self.webView?.removeFromSuperview() - let webView = PortalUIView(portal: portal._portal) + let webView = PortalUIView(portal: portal._portal.configuring(\.isWebDebuggable, webContentsDebuggingEnabled)) webView.translatesAutoresizingMaskIntoConstraints = false self.addSubview(webView) NSLayoutConstraint.activate([ @@ -74,4 +74,18 @@ class PortalView: UIView { } } } + + @objc var webContentsDebuggingEnabled: Bool = { + #if DEBUG + true + #else + false + #endif + }() { + didSet { + if #available(iOS 16.4, *) { + self.webView?.bridge.webView?.isInspectable = webContentsDebuggingEnabled + } + } + } } diff --git a/src/index.tsx b/src/index.tsx index 3aa95cf..a32fc6c 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -172,7 +172,11 @@ export interface AssetMap { /** * Props needed for rendering a {@link Portal} */ -export type PortalProps = { portal: Portal; webVitals?: WebVitals } & ViewProps; +export type PortalProps = { + portal: Portal; + webVitals?: WebVitals; + webContentsDebuggingEnabled?: boolean; +} & ViewProps; export interface LiveUpdate { /** The AppFlow application ID */