Skip to content

[Bug]: iOS with golang static lib webview load failure #7844

Open
@betamos

Description

@betamos

Capacitor Version

💊 Capacitor Doctor 💊

Latest Dependencies:

@capacitor/cli: 7.0.1
@capacitor/core: 7.0.1
@capacitor/android: 7.0.1
@capacitor/ios: 7.0.1

Installed Dependencies:

@capacitor/android: not installed
@capacitor/cli: 7.0.1
@capacitor/core: 7.0.1
@capacitor/ios: 7.0.1

[success] iOS looking great! 👌

Other API Details

➜  mobile git:(cap) ✗ npm --version 
11.0.0
➜  mobile git:(cap) ✗ node --version
v23.6.0
➜  mobile git:(cap) ✗ pod --version
1.16.2
➜  mobile git:(cap) ✗ go version
go version go1.23.4 darwin/arm64

Platforms Affected

  • iOS
  • Android
  • Web

Current Behavior

I have custom native code to add a gomobile static lib as a framework. When I switched to use my bundled site in public for prod, it failed with:

⚡️  WebView failed provisional navigation
⚡️  Error: The file “public” couldn’t be opened.

This happens in both simulator and physical iPhone (both iOS 18.2).

This error returns exactly one search result: #6974 . It's closed, but nevertheless:

  • It's suggested there's a workaround, but not how? I share mine below.
  • The Go team blames Apple (Foundation Framework) and proceeds to stick head into sand
  • Tim Apple isn't even informed, afaik
  • In development with HMR, I didn't notice this because if there's a serverUrl it's not triggered

The gomobile command used was: gomobile bind -ldflags="-s" -target ios -tags ios -o ./ios/App/Pld.xcframework

Expected Behavior

Webview should load normally & peacefully.

Project Reproduction

it's not easy to do atm...

Additional Information

I managed to find a workaround through MyViewController (see docs for how to set it up). The actual fix is a one-liner, but to set it up is quite tedious, especially if you're not already using a custom view controller.

import UIKit
import Capacitor

public struct CustomRouter: Router {
    public init() {}
    public var basePath: String = ""
    public func route(for path: String) -> String {
        // FIX: Never pass an empty string here
        let pathUrl = URL(fileURLWithPath: path.isEmpty ? "/" : path)

        // If there's no path extension it also means the path is empty or a SPA route
        if pathUrl.pathExtension.isEmpty {
            return basePath + "/index.html"
        }

        return basePath + path
    }
}

class MyViewController: CAPBridgeViewController {
    
    // CRITICAL: Must pass our own router otherwise *no bueno* 
    override open func router() -> any Router {
        CustomRouter()
    }
    override open func capacitorDidLoad() {
        // Here I register my plugin, but the failure occurs even if I don't register it.
        bridge?.registerPluginInstance(PldPlugin())
    }
}

My understanding is that the non-deterministic behavior of URL(fileURLWithPath:) occurs when the path is empty. Normally an empty path translates to file:/// which is equivalent to passing /.

God knows what combination of things causes this to happen, but if Capacitor can avoid this non-determinism perhaps that's an improvement? It certainly is for me (and clearly, whoever posted that other issue). Happy to send a PR if so.

(Obligatory thanks for a great framework. I'm new to mobile dev and I can't thank the contributors enough.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs reproductionneeds reproducible example to illustrate the issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions