当通过 macOS 本机应用程序内的 SwiftUI WKWebView 调用时,如何使文件打开对话框起作用?

How do I get the file open dialog to work, when called through a SwiftUI WKWebView inside a macOS native app?

我目前 运行 是一个用 SwiftUI 制作的简单 WkWebView - 包含我能够从 中瞥见的所有建议。它打开了一个网站。该网站有一个浏览器按钮。如果我在 Safari 上单击浏览器按钮,将打开一个 Finder window。如果我在使用 Xcode 创建的 .app 中做同样的事情,Finder window 不会打开。

这是我在沙盒中启用的内容:

我用的代码是这个(全部来自ContentView.swift)

import SwiftUI
import WebKit


public struct WebBrowserView {

    private let webView: WKWebView = WKWebView()

    // ...

    public func load(url: URL) {
        webView.load(URLRequest(url: url))
    }

    public class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate {

        var parent: WebBrowserView

        init(parent: WebBrowserView) {
            self.parent = parent
        }

        public func webView(_: WKWebView, didFail: WKNavigation!, withError: Error) {
            // ...
        }

        public func webView(_: WKWebView, didFailProvisionalNavigation: WKNavigation!, withError: Error) {
            // ...
        }

        public func webView(_: WKWebView, didFinish: WKNavigation!) {
            // ...
        }

        public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
            // ...
        }

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

        public func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
            if navigationAction.targetFrame == nil {
                webView.load(navigationAction.request)
            }
            return nil
        }
    }

    public func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }
}


#if os(macOS) // macOS Implementation (iOS version omitted for brevity)
extension WebBrowserView: NSViewRepresentable {

    public typealias NSViewType = WKWebView

    public func makeNSView(context: NSViewRepresentableContext<WebBrowserView>) -> WKWebView {

        webView.navigationDelegate = context.coordinator
        webView.uiDelegate = context.coordinator
        return webView
    }

    public func updateNSView(_ nsView: WKWebView, context: NSViewRepresentableContext<WebBrowserView>) {

    }
}
#endif

struct BrowserView: View {

    private let browser = WebBrowserView()

    var body: some View {
        HStack {
            browser
                .onAppear() {
                    self.browser.load(url: URL(string: "http://specificwebsitewithbrowserbutton")!)
                }
        }
        .padding()
    }
}


struct ContentView: View {

    @State private var selection = 1

    var body: some View {
                BrowserView()
                .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

那么,我如何给这个 .app 足够的权限,以便当我点击网站上的 "Upload" 按钮时,Finder window 会打开?

您必须实现以下 WKUIDelegate 委托方法

   /** @abstract Displays a file upload panel.
     @param webView The web view invoking the delegate method.
     @param parameters Parameters describing the file upload control.
     @param frame Information about the frame whose file upload control initiated this call.
     @param completionHandler The completion handler to call after open panel has been dismissed. Pass the selected URLs if the user chose OK, otherwise nil.

     If you do not implement this method, the web view will behave as if the user selected the Cancel button.
     */
    @available(OSX 10.12, *)
    optional func webView(_ webView: WKWebView, runOpenPanelWith 
                  parameters: WKOpenPanelParameters, initiatedByFrame frame: WKFrameInfo, 
                  completionHandler: @escaping ([URL]?) -> Void)

这是实现的例子

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)
        }
    }
}