7

I have created an OSX application in Swift and embedded a WebView in it using Xcode 7. I have a file selector on the web page loaded by the WebView which asks the user to browse for a particular file from the user's computer. The problem I am facing is that nothing happens when the user clicks on the browse button.

The same file selector control is working fine if i open the same web page in Safari.

I am relatively new in swift, so help in this case would be appreciated.

Here is my viewDidLoad function:

func viewDidLoad() {
    super.viewDidLoad() 
    let url=NSURL(string: "http://video.online-convert.com/convert-to-mp4")
    let request=NSURLRequest(URL:url!) 
    webView.frameLoadDelegate=self 
    webView.mainFrame.loadRequest(request)  
    webView.shouldCloseWithWindow = true 
    webView.drawsBackground = true 
}

Thanks.

Maneesh Raina
  • 125
  • 1
  • 7
  • This probably has something to do with how the URL object is created but could you show us some of your code please. It makes it easier for us to pinpoint the problem :) – pbodsk Mar 22 '17 at 10:08
  • "video.online-convert.com/convert-to-mp4" is not the path to your local html page is it? – pbodsk Mar 22 '17 at 11:44
  • But if I understand you correct, you would like to open a _local_ html page (a page that is located on your own machine) in a webview right? Or am I misunderstanding your question? – pbodsk Mar 22 '17 at 11:49
  • yes you are correct. my project is to open a local html page residing in my computer. The local html page has a browse file button in it which allows the user to select a text file from his local computer for some processing. However when my browse file button was not working i tried with an external site just to see if the error was in my page or with WebView. The result was the same. input type=file element are not responding in WebVew. What could be the reason? – Maneesh Raina Mar 22 '17 at 11:55

1 Answers1

7

I have not worked with this myself, but from puzzling bits and pieces together this seems to do what you'd like.

First...I'm using a WKWebView. That is declared and initialised with a local HTML file like so:

import Cocoa
import WebKit

class ViewController: NSViewController {

    let webview: WKWebView = WKWebView()

    override func viewDidLoad() {
        super.viewDidLoad()
        webview.autoresizingMask = [.viewWidthSizable, .viewHeightSizable]
        webview.frame = view.bounds
        webview.uiDelegate = self
        view.addSubview(webview)

        let fileURL = URL(fileURLWithPath: "/Users/myuserhere/Desktop/index.html")
        webview.loadFileURL(fileURL, allowingReadAccessTo: fileURL)
     }
  }

The interesting part is webview.uiDelegate. This promises that we will conform to the WKUIDelegate protocol documented here. As it says:

The WKUIDelegate class provides methods for presenting native user interface elements on behalf of a webpage.

One of the methods you can implement is runOpenPanelWithParameters:

If you implement this method, you promise that you will present a file upload panel and call the callback method of this method with the outcome of what the user selected. Remember to also call the callback method when the user presses cancel.

Here is a quick and dirty example:

extension ViewController: WKUIDelegate {
    func webView(_ webView: WKWebView, runOpenPanelWith parameters: WKOpenPanelParameters, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping ([URL]?) -> Void) {
        let openPanel = NSOpenPanel()
        openPanel.canChooseFiles = true
        openPanel.begin { (result) in
            if result == NSApplication.ModalResponse.OK {
                if let url = openPanel.url {
                    completionHandler([url])
                }
            } else if result == NSApplication.ModalResponse.cancel {
                completionHandler(nil)
            }
        }
    }
}

Hopefully that gives you something to get started with.

Bonus Material

Here are some links that helped me:

How to upload files from WKWebView

How to implement the delegate method

How to create a NSOpenPanel in Swift

For iOS (asked by @DarshanMothreja)

I tried gluing together a simple program to do the same on iOS. I hope it is useful to you @DarshanMothreja

HTML A file called index.html is added to the Xcode project. The content looks like this:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Test</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <label for="file">File goes here</label>
    <input type="file" name="file" value="File">
  </body>
</html>

Swift

Here is the ViewController

import UIKit
import WebKit

class ViewController: UIViewController {
    let webview: WKWebView = WKWebView()

    override func viewDidLoad() {
        super.viewDidLoad()
        webview.autoresizingMask = [ .flexibleWidth, .flexibleHeight ]
        webview.frame = view.bounds
        webview.uiDelegate = self
        view.addSubview(webview)   
    
        if let path = Bundle.main.path(forResource: "index", ofType: "html") {
            let fileURL = URL(fileURLWithPath: path)
            webview.loadFileURL(fileURL, allowingReadAccessTo: fileURL)
        }
    }
}

extension ViewController: WKUIDelegate { }

If I run the above, I get this result when I tap the "Choose File" button, no need to add any delegate methods.

Image picker is go

Hope that gives you something to work with.

Community
  • 1
  • 1
pbodsk
  • 6,787
  • 3
  • 21
  • 51
  • 1
    Can you suggest for iOS – Darshan Mothreja Nov 21 '18 at 05:41
  • @DarshanMothreja maybe :) What are you trying to achieve? – pbodsk Nov 21 '18 at 08:27
  • I am opening an webpage in WKWebkit, So there is an upload button given. Clicking on that button nothing happens I am unable to upload image/file from device. – Darshan Mothreja Nov 21 '18 at 13:39
  • Great answer, but there's still an important issue: the file selection window won't appear again if you hit the cancel button!! Any suggestions?? – Heitor Jun 09 '19 at 03:22
  • 1
    @Heitor Thank you, glad it helped you. You have this problem on iOS I suppose? I don't have any obvious suggestions/solutions, but maybe look into what cancel does...I mean, do you need to reload the page on cancel for instance. Sorry, its the best I can think of right now – pbodsk Jun 10 '19 at 08:53
  • 1
    It's MacOS. I'm looking for a solution in the Swift side, not the html/js side...the bug's cancel button is in the upload window, who is not js I suppose. – Heitor Jun 11 '19 at 05:36
  • 1
    @Heitor OK, I looked into it again. If you press the cancel button in the `openPanel` you can see this error in the console `[NSVBSavePanel observeValueForKeyPath:ofObject:change:context:] caught non-fatal NSInternalInconsistencyException 'Completion handler passed to -[WebTest.ViewController webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:] was not called' with backtrace`. That means that the callback was not called when the openDialog was dismissed. So we must check for that scenario too. I've updated my answer, look for the `NSApplication.ModalResponse.cancel` part – pbodsk Jun 11 '19 at 09:01
  • Fantastic man, it worked like a charm, thx so much! I'd just add an "else if" there to make it more elegant! ;) – Heitor Jun 14 '19 at 06:41