swiftnoob
swiftnoob

Reputation: 413

SwiftUI: WebKit toolbar buttons not doing anything

This is a web browser app for macOS made with SwiftUI and WebKit. In my address toolbar i have shortcut buttons such as refresh, go back, go forward and a shortcut link button to YouTube. but clicking on any of them nothing happens.

ContentView

struct ContentView: View {
    @StateObject var webModel = WebStateModel()
    @State var text = ""
    
    var body: some View {
   //toolbar
            HStack{
                //Refresh Button
                 Button(action: {
                    WebView(webModel: webModel).refresh()    
                     })  {
                         Image("arrow.left.circle")
                     }
                 //Go Forward Button
                 Button{
                    WebView(webModel: webModel).goForward()

                 } label: {
                     Image("arrow.right.circle")
                     }              
                 //Youtube button
                 Button{
                    WebView(webModel: webModel).youtube()
                         } label: {
                             Image("house.circle")
                             }
            }
        VStack (spacing: 80) {
            TextField("Enter a URL", text: Binding(
                get: { text },
                set: { text = WebStateModel.stripHttps($0) } ), onCommit: {
                    webModel.updateUrl(text)
                })
            WebView(webModel: webModel) //To display the loaded web page
        }
    }
}

View Model of the WebView

class WebStateModel: ObservableObject {
    @Published var url: URL? = URL(string: "https://www.google.com")
    
    func updateUrl(_ str: String) {
        if let theUrl = URL(string: "https://" + WebStateModel.stripHttps(str)) {
            url = theUrl
        }
    }
    
    static func stripHttps(_ str: String) -> String {
        var txt = str.trim()
        if txt.starts(with: "https://") {
            txt = String(txt.dropFirst(8))
        }
        return txt
    }  
}

WebView containing the shortcut functions for the toolbar.

struct WebView: NSViewRepresentable {
    @ObservedObject var webModel: WebStateModel

    func makeNSView(context: Context) -> WKWebView {
        let wkWebview = WKWebView()
        if let theUrl = webModel.url {
            let request = URLRequest(url: theUrl, cachePolicy: .returnCacheDataElseLoad)
            wkWebview.load(request)
        }
        return wkWebview
    }

    func updateNSView(_ nsView: WKWebView, context: Context) {
        if let theUrl = webModel.url {
            let request = URLRequest(url: theUrl, cachePolicy: .returnCacheDataElseLoad)
            nsView.load(request)
        }
    }
func refresh() {
    let wkWebview = WKWebView()
    wkWebview.reload()
}


func goBack() {
    let wkWebview = WKWebView()
    guard wkWebview.canGoBack else { return }
    wkWebview.goBack()
}


func goForward() {
    let wkWebview = WKWebView()
    guard wkWebview.canGoForward else { return }
    wkWebview.goBack()
}

func youtube() {
    let wkWebview = WKWebView()
         wkWebview.load(URLRequest(url: (URL(string: "https://www.youtube.com")!)))
}
}

EDIT i've also tried

func refresh() {
        WKWebView().reload()
    }

    func goBack() {
        guard WKWebView().canGoBack else { return }
        WKWebView().goBack()
    }


    func goForward() {
        guard WKWebView().canGoForward else { return }
        WKWebView().goForward()
    }

    func youtube() {
        WKWebView().load(URLRequest(url: (URL(string: "https://www.youtube.com")!)))
    }

And nothing happens in the WebView when clicking any of the buttons in the toolbar.

Upvotes: 0

Views: 1044

Answers (1)

as mentioned, you are creating a new object every time you call a function. Also you should not try to "return" a view inside the buttons. SwiftUI is based on changing state values.

Do this instead:

import SwiftUI
import WebKit

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class WebStateModel: ObservableObject {
    @Published var url: URL? = URL(string: "https://www.google.com")
    
    func updateUrl(_ str: String) {
        if let theUrl = URL(string: "https://" + WebStateModel.stripHttps(str)) {
            url = theUrl
        }
    }
    
    static func stripHttps(_ str: String) -> String {
        var txt = str.trim()
        if txt.starts(with: "https://") {
            txt = String(txt.dropFirst(8))
        }
        return txt
    }
}

struct WebView: NSViewRepresentable {
    @ObservedObject var webModel: WebStateModel
    
    let wkWebview = WKWebView()
    
    func makeNSView(context: Context) -> WKWebView {
        if let theUrl = webModel.url {
            let request = URLRequest(url: theUrl, cachePolicy: .returnCacheDataElseLoad)
            wkWebview.load(request)
        }
        return wkWebview
    }
    
    func updateNSView(_ nsView: WKWebView, context: Context) {
        if let theUrl = webModel.url {
            let request = URLRequest(url: theUrl, cachePolicy: .returnCacheDataElseLoad)
            nsView.load(request)
        }
    }
    func refresh() {
        wkWebview.reload()
    }
    
    func goBack() {
        guard wkWebview.canGoBack else { return }
        wkWebview.goBack()
    }
    
    func goForward() {
        guard wkWebview.canGoForward else { return }
        wkWebview.goForward()
    }
    
    func youtube() {
        wkWebview.load(URLRequest(url: (URL(string: "https://www.youtube.com")!)))
    }
}

extension String {
    func trim() -> String {
        return self.trimmingCharacters(in: .whitespacesAndNewlines)
    }
}

struct ContentView: View {
    @StateObject var webModel = WebStateModel()
    @State var text = ""
    
    @State var webView: WebView?
    
    var body: some View {
        VStack {
            //toolbar
            HStack{
                Button(action: { webView?.refresh() }) {
                    Image(systemName: "arrow.clockwise.circle")
                }
                Button(action: { webView?.goBack() }) {
                    Image(systemName: "arrow.left.circle")
                }
                Button(action: { webView?.goForward() }) {
                    Image(systemName: "arrow.right.circle")
                }
                Button(action: { webView?.youtube() }) {
                    Image(systemName: "house.circle")
                }
            }
            
            VStack (spacing: 80) {
                TextField("Enter a URL", text: Binding(
                    get: { text },
                    set: { text = WebStateModel.stripHttps($0) } ), onCommit: {
                        webModel.updateUrl(text)
                    })
                webView
            }
        }
        .onAppear {
            webView = WebView(webModel: webModel)
        }
    }
    
}

Upvotes: 1

Related Questions