Skip to content

fix(mobile): return browser launch errors - #5916

Open
mortenolsrud wants to merge 2 commits into
wailsapp:masterfrom
mortenolsrud:fix/mobile-openurl-errors
Open

fix(mobile): return browser launch errors#5916
mortenolsrud wants to merge 2 commits into
wailsapp:masterfrom
mortenolsrud:fix/mobile-openurl-errors

Conversation

@mortenolsrud

@mortenolsrud mortenolsrud commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Description

Mobile OpenURL currently returns nothing on Android and iOS. Android posts startActivity and only logs exceptions, while iOS discards the openURL completion result. Callers therefore cannot distinguish a successful browser launch from an invalid URL, missing handler, or rejected launch.

This changes the shared contract to:

OpenURL(url string) error

Android now validates URI syntax, performs the activity launch on the main thread, and returns launch exceptions through the existing JNI string bridge. iOS validates the URL and returns the asynchronous UIApplication.openURL completion result to Go. Both platforms use bounded waits so a stalled UI thread cannot block the caller indefinitely.

The desktop Mobile stub returns an unsupported-platform error. The generated Android host, checked-in mobile example, documentation, and example error handling are updated together. Focused source-contract tests protect the native bridge signatures and failure paths.

This was surfaced by PKCE browser flows: silently failing to launch a browser leaves authentication waiting for a callback that can never arrive. The behavior is general to all Wails mobile applications and does not depend on a particular authentication library.

Compatibility note

Mobile support is experimental, but this changes the exported MobileManager method signature and therefore requires custom implementations to add the error result. Ordinary callers that invoke OpenURL(...) as a statement continue to compile.

Existing generated Android projects contain the old void openURL(String) host method. Because generated Java hosts are not updated automatically, those projects must regenerate the Android host or synchronize WailsBridge.java when upgrading.

No matching open issue was found.

Type of change

  • Bug fix (non-breaking for ordinary callers)
  • New feature
  • Experimental API contract change
  • Documentation update

How Has This Been Tested?

  • go test -count=1 ./internal/commands ./pkg/application
  • Verified the canonical and checked-in example Android bridge files are identical.
  • Source-contract regression coverage for Android validation, UI-thread dispatch, timeout/cancellation, and returned failures.
  • Source-contract regression coverage for iOS URL validation, completion rejection, timeout, and C/Go ownership signature.
  • Council-style review covered native threading, JNI reflection, Objective-C ARC ownership, API compatibility, and test coverage; findings were applied and re-reviewed.

Honest caveats:

  • Android and iOS launches were not exercised on a device from this Linux host.

  • The standalone mobile example module currently requests an unrelated go mod tidy, so its module-wide test was not allowed to modify metadata.

  • Windows

  • macOS

  • Linux

Test Configuration

  • Go: go1.26.5
  • OS: Ubuntu 24.04.4 LTS, amd64

Checklist:

  • (v2 only) changelog update — n/a; v3 entries are automated
  • My code follows the general coding style of this project
  • I have performed a self-review of my own code
  • I have made corresponding documentation changes
  • My changes generate no new warnings in the tested packages
  • I have added tests that prove the fix is represented in generated/native sources
  • New and existing focused unit tests pass locally

Summary by CodeRabbit

  • New Features

    • Mobile URL opening now validates URLs and reports launch failures, interruptions, timeouts, and unavailable handlers.
    • Android and iOS URL launches provide clear success or error results.
    • Desktop platforms now explicitly report that URL opening is unsupported.
  • Documentation

    • Updated mobile API documentation with error behavior, URL validation, and platform capability details.
  • Tests

    • Added coverage for unsupported desktop behavior and mobile URL launch failures.

@github-actions github-actions Bot added Documentation Improvements or additions to documentation cli v3-alpha labels Aug 6, 2026
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 596be8ff-ba91-46d0-b93c-4d040ee49e9e

📥 Commits

Reviewing files that changed from the base of the PR and between 34e1814 and 85be53a.

📒 Files selected for processing (2)
  • v3/pkg/application/mobile_features_ios.m
  • v3/pkg/application/mobile_features_source_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • v3/pkg/application/mobile_features_source_test.go
  • v3/pkg/application/mobile_features_ios.m

Walkthrough

Mobile OpenURL methods now return errors across Android, iOS, and desktop stubs. Native bridges validate URLs, report launch failures, and use timed synchronous result handling. Examples, documentation, generated assets, and source tests reflect the new contract.

Changes

Mobile API contract and desktop behavior

Layer / File(s) Summary
API contract and desktop behavior
v3/pkg/application/mobile.go, v3/pkg/application/mobile_stub.go, v3/pkg/application/mobile_stub_test.go, v3/ANDROID.md, v3/IOS.md, v3/pkg/application/mobile_features_ios.h
MobileManager.OpenURL now returns an error. The desktop stub reports that the operation is unavailable. Android and iOS documentation describe validation and launch failures. The iOS section comment now uses “actions”.

Android launch validation and propagation

Layer / File(s) Summary
Android launch validation and propagation
v3/internal/commands/build_assets/android/.../WailsBridge.java, v3/examples/mobile/build/android/.../WailsBridge.java, v3/pkg/application/mobile_features_android.go, v3/internal/commands/mobile_openurl_test.go, v3/examples/mobile/native_features_android.go
Android bridge implementations validate URLs, launch browsers on the main thread, wait up to 30 seconds, and return launch errors. The Go manager and example handler propagate and log failures. Source tests verify both implementations.

iOS launch validation and propagation

Layer / File(s) Summary
iOS launch validation and propagation
v3/pkg/application/mobile_features_ios.m, v3/pkg/application/mobile_features_ios.go, v3/pkg/application/mobile_features_source_test.go, v3/examples/mobile/native_features_ios.go
The iOS bridge validates URLs, waits for completion results up to 30 seconds, and returns allocated error strings. The Go manager and example handler propagate and log failures. Source tests verify error handling and the removal of canOpenURL.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GoCaller as application.Android.OpenURL or application.IOS.OpenURL
  participant NativeBridge as WailsBridge.openURL or ios_open_url
  participant PlatformHandler as Android or iOS URL handler
  GoCaller->>NativeBridge: URL string
  NativeBridge->>PlatformHandler: validated URL launch
  PlatformHandler-->>NativeBridge: launch result or failure
  NativeBridge-->>GoCaller: error status
Loading

Possibly related PRs

Poem

I’m a rabbit with a URL to share,
Errors now travel everywhere.
Android waits, iOS replies,
Desktop reports when support denies.
Tests guard each launch with care.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: mobile browser launch failures now return errors.
Description check ✅ Passed The description explains the change, compatibility impact, testing, limitations, and checklist status in sufficient detail.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@v3/pkg/application/mobile_features_ios.m`:
- Around line 113-115: Update the URL-opening flow around the main-queue block
and dispatch_semaphore_wait to use synchronized cancellation state shared by
both paths. Have the main-queue block check and claim the state before calling
openURL, and have the timeout path mark the operation cancelled before
returning, ensuring a queued block cannot launch the URL after timeout.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 45a6fdf8-7b65-4a7a-91b6-6392de3d160e

📥 Commits

Reviewing files that changed from the base of the PR and between f80dad5 and 34e1814.

📒 Files selected for processing (15)
  • v3/ANDROID.md
  • v3/IOS.md
  • v3/examples/mobile/build/android/app/src/main/java/com/wails/app/WailsBridge.java
  • v3/examples/mobile/native_features_android.go
  • v3/examples/mobile/native_features_ios.go
  • v3/internal/commands/build_assets/android/app/src/main/java/com/wails/app/WailsBridge.java
  • v3/internal/commands/mobile_openurl_test.go
  • v3/pkg/application/mobile.go
  • v3/pkg/application/mobile_features_android.go
  • v3/pkg/application/mobile_features_ios.go
  • v3/pkg/application/mobile_features_ios.h
  • v3/pkg/application/mobile_features_ios.m
  • v3/pkg/application/mobile_features_source_test.go
  • v3/pkg/application/mobile_stub.go
  • v3/pkg/application/mobile_stub_test.go

Comment on lines +113 to +115
long waitResult = dispatch_semaphore_wait(
completed, dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC));
if (waitResult != 0) return mfDup(@"timed out waiting for application to open URL");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Prevent a URL launch after a timeout.

If the main queue is blocked for 30 seconds, this function returns a timeout error but leaves the queued block active. When the main queue resumes, the block still calls openURL. The caller then receives a failure result while the external URL launches later.

Add synchronized cancellation state. The main-queue block must check and claim that state before it calls openURL. The timeout path must mark the operation cancelled before it returns.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/pkg/application/mobile_features_ios.m` around lines 113 - 115, Update the
URL-opening flow around the main-queue block and dispatch_semaphore_wait to use
synchronized cancellation state shared by both paths. Have the main-queue block
check and claim the state before calling openURL, and have the timeout path mark
the operation cancelled before returning, ensuring a queued block cannot launch
the URL after timeout.

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

Labels

cli Documentation Improvements or additions to documentation v3-alpha

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

2 participants