capacitor icon indicating copy to clipboard operation
capacitor copied to clipboard

bug: Blob downloads don't work on iOS & Android

Open gwdp opened this issue 2 years ago • 12 comments

Bug Report

Capacitor Version

Latest Dependencies:

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

Installed Dependencies:

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

[success] iOS looking great! 👌
[success] Android looking great! 👌

Platform(s)

  • iOS (Tested on iOS 13.x, 14.x and 15.2 )
  • Android (tested on Android 11, API 29)

Current Behavior

iOS 15>= (tested on few 15.x different versions) Whenever browser is asked to navigate to a URL such as blob:capacitor://localhost/ee26ec26-911c-461e-b01e-dd715c99e54a something like:

XX XXXX V1.1[59585:3775699] [Process] 0x15684b018 - [pageProxyID=5, webPageID=6, PID=59586] WebPageProxy::didFailProvisionalLoadForFrame: frameID = 3, domain = WebKitErrorDomain, code = 102

is printed out and nothing happens.

iOS 13.2.2, 13.0, 14.0.1 & 14.5 - (safe to assume this for 14.5 =<) Error is different:

Failed to open URL blob:https://jimmywarting.github.io/29dbf607-c3d4-49a3-baa2-977d71cb3b80: Error Domain=NSOSStatusErrorDomain Code=-10814 "(null)" UserInfo={_LSLine=247, _LSFunction=-[_LSDOpenClient openURL:options:completionHandler:]}

Android Nothing happens, more investigation is needed to see technical details of the implementation.

Expected Behavior

Allow that blob to be downloaded without errors into a standard download directory (as Safari does). Maybe, an even better solution would be asking the user where to save it. (optionally) By RFC these URLs should be handled and downloaded by the browser. More details at: https://w3c.github.io/FileAPI/#url-intro

Code Reproduction

iOS

  • Download from repo and install deps: https://github.com/ikon-integration/Capacitor-Blob-Download-Issue
  • Open on Xcode npx capacitor open ios, run from there
  • After the app is loaded, click on the start button. (careful, button on this sample seems pretty small on the simulator, so better to zoom on the webview)
  • Error should be printed on the console

Alternatively: (with any Capacitor app you might have around)

  • Set your capacitor.config.json server.url to https://jimmywarting.github.io/StreamSaver.js/examples/saving-multiple-files.html (example)
  • Zoom in to be able to see the start button, and click on it.
  • Error should be available on the console

Android Same from iOS, however, no error is displayed and the file is not visible anywhere else on the system.

Other Technical Details

iOS I believe this is not related to any versioning-related problem as it seems iOS 15 significantly improved the support of download/blob on the WKWebViewDelegate. With some tweaks on the WebViewDelegationHandler, I was able to make it work and download to the downloads or documents folder seamlessly.

Android Still, more investigation is needed on the Android side, but RFC described behaviour should also be

Additional Context

By RCF blob: URL schema should trigger the download of the URL being provided. I believe this will enhance Capacitor support to already existing apps as developers expect the same behaviour to be followed.

Possible fix

Changes on theWebViewDelegationHandler like the one below will handle it for iOS > 14.5, however still unsure where it should be saved as a default and how to allow developers to overwrite this behaviour. Another important factor is the attempt of making it to work with previous iOS versions for sure. Creating a PR soon and linking into this issue. (Changes will be certainly be needed)

    ...minor changes above...
    public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {

        // post a notification for any listeners
        NotificationCenter.default.post(name: .capacitorDecidePolicyForNavigationAction, object: navigationAction)
        
        if #available(iOS 14.5, *) {
            if (navigationAction.shouldPerformDownload) {
                print("Is download");
                decisionHandler(.download)
                return;
            }
        }
       .....
    }
    @available(iOS 14.5, *)
    func webView(_ webView: WKWebView, navigationAction: WKNavigationAction, didBecome download: WKDownload) {
        download.delegate = self;
    }
    @available(iOS 14.5, *)
    func webView(_ webView: WKWebView, navigationResponse: WKNavigationResponse, didBecome download: WKDownload) {
        download.delegate = self;
    }

    public func download(_ download: WKDownload, decideDestinationUsing response: URLResponse, suggestedFilename: String, completionHandler: @escaping (URL?) -> Void) {
        print("decide destination", suggestedFilename);
        
        let documentsURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
        let documentURL = documentsURL.appendingPathComponent(suggestedFilename, isDirectory: false)
        print(documentURL.absoluteString)
        completionHandler(documentURL);
    }
    ....minor changes below....

Edited to Improve issue with reproducible code, android behaviour and other small improvements

gwdp avatar Mar 02 '22 19:03 gwdp

@jcesarmobile Could you please give a check on this?

gwdp avatar Mar 18 '22 20:03 gwdp

Same issue here

marcelklehr avatar Mar 19 '22 13:03 marcelklehr

bump, I'm not sure if the PR for this works entirely but this functionality is necessary and expected out of the box

rowrowrowrow avatar Nov 24 '22 19:11 rowrowrowrow

In the console on Android I see this error: Not allowed to load local resource: blob:https://

giuseppeilromano avatar Nov 30 '22 09:11 giuseppeilromano

same here

SergioSuarezDev avatar Dec 10 '22 19:12 SergioSuarezDev

Also experiencing the same error when attempting to download a file from a data url data:image/png;base64

c12i avatar Apr 27 '23 08:04 c12i

1 year after, and the problem is not solved :(

SergioSuarezDev avatar May 02 '23 21:05 SergioSuarezDev

Same error on iOS

atrujillofalcon avatar May 09 '23 02:05 atrujillofalcon

Is there any solution?

kalanda avatar May 24 '23 09:05 kalanda

Any update on this?

reinos avatar Sep 22 '23 07:09 reinos

Any update on this?

seemsindie avatar Oct 03 '23 15:10 seemsindie

Please, see https://github.com/ionic-team/capacitor/pull/5498#issuecomment-1470551715 for the latest info on this.

gwdp avatar Oct 03 '23 16:10 gwdp