0

I want to create a web browser that if a link opens in a new window it will open a new window (tab) with the new link.

struct WebBrowserApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    @StateObject private var webBrowserModel = WebBrowserModel()
    
    var body: some View {
        WebBrowser(webView: webBrowserModel.webView)
            .frame(minWidth: 600, maxWidth: .infinity, minHeight: 800, maxHeight: .infinity)
            .onAppear() {
                webBrowserModel.webView.load(URLRequest(url: URL(string: "https://www.w3schools.com/jsref/met_win_prompt.asp")!))
            }
    }
}

struct WebBrowser: NSViewRepresentable {
    typealias NSViewType = WKWebView
    
    private let webView: WKWebView
    
    init(webView: WKWebView) {
        self.webView = webView
    }
    
    func makeNSView(context: Context) -> WKWebView {
        return self.webView
    }
    
    func updateNSView(_ webView: WKWebView, context: Context) {}
}

class WebBrowserModel: NSObject, ObservableObject, WKUIDelegate {
    lazy public var webView: WKWebView = {
        let configuration = WKWebViewConfiguration()
        let webView = WKWebView(frame: CGRect.zero, configuration: configuration)
        webView.uiDelegate = self
        return webView
    }()
    
    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
        // Open new window-tab
        guard let originWindow = NSApp.keyWindow, let originWindowController = originWindow.windowController else {
            return nil
        }
        originWindowController.newWindowForTab(nil)
        guard let newWindow = NSApp.keyWindow, originWindow != newWindow else {
            return nil
        }
        originWindow.addTabbedWindow(newWindow, ordered: .above)

        // How to load the new link: navigationAction.request?

        return nil
    }
}
  • It will take the content within WindowGroup as a template to create the new window.
  • I have the new url in navigationAction.request.
  • So how am I getting the new link to open in the webView of the new window?

(Edit V2) With inspiration of the comments of @Asperi I created the following sample. I made a static var accessable from anywhere.

struct WebBrowserAppConfig {
    static var startURL: URL? = nil
}

I made a helper for creating a new window tab.

extension NSApplication {
    static func addTabbedWindowFromKeyWindow(ordered: NSWindow.OrderingMode) {
        guard let originWindow = NSApp.keyWindow, let originWindowController = originWindow.windowController else {
            return
        }
        originWindowController.newWindowForTab(nil)
        guard let newWindow = NSApp.keyWindow, originWindow != newWindow else {
            return
        }
        originWindow.addTabbedWindow(newWindow, ordered: ordered)
    }
}

I then set the configuration in the web delegate.

func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    WebBrowserAppConfig.startURL = navigationAction.request.url
    NSApplication.addTabbedWindowFromKeyWindow(ordered: .above)
    return nil
}

I then access it within the SwiftUI view.

struct ContentView: View {
    @StateObject private var webBrowserModel = WebBrowserModel()

    var body: some View {
        WebBrowser(webView: webBrowserModel.webView)
            .frame(minWidth: 600, maxWidth: .infinity, minHeight: 800, maxHeight: .infinity)
            .onAppear() {
                if let url = WebBrowserAppConfig.startURL {
                    webBrowserModel.webView.load(URLRequest(url: url))
                } else {
                    webBrowserModel.webView.load(URLRequest(url: URL(string: "https://www.w3schools.com/jsref/met_win_prompt.asp")!))
                }
            }
    }
}

This works, although it feels really hacky.

Mark
  • 16,906
  • 20
  • 84
  • 117
  • You should specify which version of SwiftUI you're working on (ie OS target). This changes dramatically in macOS Ventura, for example. – jnpdx Jul 10 '22 at 16:09
  • Does this answer your question https://stackoverflow.com/a/62916025/12299030? Or this one https://stackoverflow.com/a/67363964/12299030? – Asperi Jul 10 '22 at 16:12
  • @jnpdx you are correct (for the better luckily). I was looking for a solution in the current version XCode 13, MacOS 12. – Mark Jul 10 '22 at 16:35
  • @Asperi I am looking into it, see if I can work something out with AppState. – Mark Jul 10 '22 at 16:35
  • I made a static var hold the url I want to load and then spawn the new window and read from the static var. It feels weird, but it works. – Mark Jul 10 '22 at 16:51

0 Answers0