Skip to content

Conversation

@mhennemeyer
Copy link

Summary

This PR introduces an ErrorResponse handling feature that lets library consumers intercept and react to failed page/resource loads in the multiplatform WebView. Apps can observe structured error information and decide whether to stop loading or allow the default platform behavior to continue.

Motivation

Apps often need to:

  • Show a custom error UI (e.g., a friendly 404 screen)
  • Log/report network or TLS failures
  • Retry or navigate elsewhere upon failure
  • Prevent the default webview error page from flashing

Previously, there was no unified way to observe and control error handling across platforms. This PR provides a simple, consistent API for that use case.

Key Changes

  • New common types under com.multiplatform.webview.response:
    • ErrorResponse: encapsulates error details (platform-agnostic fields like errorCode, description, and the failing URL when available).
    • ErrorResponseInterceptor: callback interface to intercept error responses.
    • ShouldStopLoading: boolean-style contract indicating whether the WebView should stop its default error handling and further loading.
  • WebViewNavigator now accepts an errorResponseInterceptor that will be invoked when the underlying platform reports a load error.
  • Platform integrations:
    • Android: wire-up in AccompanistWebView.kt to forward WebView/Chromium errors into the interceptor.
    • iOS: wire-up in WKNavigationDelegate.kt to forward WKWebView navigation and provisional load errors.
  • Sample app additions:
    • ErrorResponseSample.kt shows how to plug in the interceptor and display the error.
    • files/samples/errorResponse.html helps demonstrate a failing navigation.
  • Documentation:
    • README section added documenting error interception with code snippet and behavior notes.

Public API

  • Package: com.multiplatform.webview.response
    • interface ErrorResponseInterceptor { fun onInterceptErrorResponse(response: ErrorResponse, navigator: WebViewNavigator): ShouldStopLoading }
    • data class ErrorResponse( val errorCode: Int, val description: String, val url: String? = null )
    • typealias ShouldStopLoading = Boolean
  • rememberWebViewNavigator(errorResponseInterceptor = …) accepts an interceptor instance.

Behavior and Semantics

  • When the platform signals a load error, the interceptor is invoked with the ErrorResponse and a WebViewNavigator reference.
  • Return value:
    • true → stop loading and suppress default error handling where possible.
    • false → allow default platform error behavior to proceed.
  • If no interceptor is provided, the existing default behavior remains unchanged.

Usage Example

val webViewNavigator = rememberWebViewNavigator(
    errorResponseInterceptor = object : ErrorResponseInterceptor {
        override fun onInterceptErrorResponse(
            response: ErrorResponse,
            navigator: WebViewNavigator
        ): ShouldStopLoading {
            // Example: show custom UI, log, or navigate elsewhere
            Logger.e("Web error: code=${response.errorCode} desc=${response.description} url=${response.url}")
            // Stop default error handling and keep control within the app
            return true
        }
    }
)

WebView(
    state = webViewState,
    navigator = webViewNavigator,
    webViewJsBridge = jsBridge,
)

Sample Screen (included)

  • sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/ErrorResponseSample.kt demonstrates:
    • Triggering an error with webViewNavigator.loadUrl("http://matthiashennemeyer.com/404-on-android-and-tls-error-on-ios")
    • Capturing the ErrorResponse in state and displaying a simple overlay
    • Returning true in the interceptor to stop further loading

Platform Notes

  • Android: hooks map WebView/Chromium error callbacks to ErrorResponse. Codes correspond to underlying Android/WebView error codes when applicable.
  • iOS: hooks map WKNavigationDelegate error callbacks. Codes correspond to NSError domain/code where available.
  • Desktop/other targets: behavior remains unchanged unless their platform-specific code also emits errors into the common API.

Backward Compatibility

  • No breaking API changes.
  • Default behavior without an interceptor is unchanged.
  • Consumers must opt in by passing an ErrorResponseInterceptor to rememberWebViewNavigator.

Testing and Verification

  • Manual/interactive testing via the new sample screen.
  • Verified both Android and iOS paths invoke the interceptor upon navigation to failing URLs.
  • Ensured that returning true prevents default error handling where platform allows.

Documentation

  • Inline KDoc added to new types.
  • README updated with “Intercepting Error Responses” section.

Limitations and Future Work

  • Error code standardization across platforms can be added later.
  • Potential to enrich context (HTTP status where available, main-frame vs subresource, etc.).
  • Desktop/WASM hooks may be implemented later for parity.

Testing Roadmap (follow-up PR)

This repository currently does not have tests. To keep this PR focused, I propose a follow-up PR that:

  • Bootstraps KMP test targets (commonTest, androidUnitTest, iosTest) with kotlin-test.
  • Adds 1–2 simple common tests to validate the setup.
  • Extracts small error-mapping helpers to enable pure unit testing (Android/iOS → ErrorResponse).
  • Optionally introduces Robolectric for Android-side callback tests and a minimal XCTest target for iOS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant