1

I have added a WKwebview in SwiftUI via UIVewRepresentable. I am having difficulties getting some buttons to make the web view go back and go forward. Below is the two classes I have made but I am not even getting print to work ( maybe a bug? ).

import SwiftUI

import WebKit

struct Webview : UIViewRepresentable {

let request: URLRequest

func makeUIView(context: Context) -> WKWebView  {
    return WKWebView()
}

func updateUIView(_ uiView: WKWebView, context: Context) {
    uiView.load(request)
}

func goBack(){
    // go back
    print("go back")
}

func goForward (){
    // go forward
    print("go forward")
}
}



import SwiftUI

struct FullWebView : View {

let webview = Webview(request: URLRequest(url: URL(string: "https://www.apple.com")!))

var body: some View {
    VStack{
        HStack{
            Button(action: {
                //do something
                self.webview.goBack()
            }){
                Text("back")
            }

            Spacer()

            Button(action: {
                //do something
                self.webview.goForward()
            }){
                Text("forward")
            }

        }

        webview
    }
}
}
Pulsarman325
  • 214
  • 3
  • 16
  • Is your specific issue getting a console verification to go back or forward, or is it with getting the WKWebView to do it? More, *where* are you trying to actually navigate backward/forward? I'd expect it's in `UIKit`, right? –  Jun 25 '19 at 20:43
  • 1
    I realised that the print method don’t work in preview only the simulator, but I still can’t work out a way to get my web view to goback – Pulsarman325 Jun 25 '19 at 20:52
  • I don't use `WKWebView`, but I'd assume web navigation needs to somehow use it, right? i'm using a `MTKView` and it's delegates in `SwiftUI` to update it's `draw` method. Are we talking something similar? Are you trying to kick off something *inside* WKWebView through its representative? –  Jun 25 '19 at 20:55
  • Look at the accepted answer to this question - hopefully it will get you going. https://stackoverflow.com/questions/56584059/send-tapaction-from-swiftui-button-action-to-uiview-function/56584470#comment99820181_56584470 –  Jun 25 '19 at 20:57
  • I’ve never used MTKView I’m afraid, so I’m not sure about that, WKWebview has methods goBack() to go back a page and goForward() to go forward a page. There is no WKWebview delegate. Im thinking I need to use context somehow?? – Pulsarman325 Jun 25 '19 at 20:59
  • I read that post earlier thanks and I used it in my code, it works as far as it calls the function but I need that function to access the view provided (WKWebview) and call a WKWebview class function from it, if that makes sense? – Pulsarman325 Jun 25 '19 at 21:03
  • I have a setup that calls `MTKView().setNeedsDisplay()`, actually an *instance* of MTKView. Does that sound like what you are trying to do? If so, what function in `WKWebView` are you trying to trigger, and is it via a SwiftUI `Button`? (I actually do this using most of the answer I pointed to. But I'll give you more specific code if I understand more of what you are doing.) –  Jun 25 '19 at 21:20
  • That does sound kind of like what I’m trying to achieve, WKWebview has a function called goBack(), im trying to call that on the WKWebview that I have loaded via a SwiftUI button – Pulsarman325 Jun 25 '19 at 21:30
  • Ouch. I was almost finished with an answer, taken from your code, when I realized you already have what I was talking about. (Sorry I didn't see this earlier.) Two more questions, both related because you *should* be getting the console prints. (1) Are you seeing www.apple.com? (2) Are you sure you are properly instantiating `WebView`? The only *real* difference I can see is in that view or struct - you aren't instantiating `WKwebView` in it. Look at the Q&A we both were looking at - maybe that's the issue. –  Jun 25 '19 at 21:50
  • Yes I am getting console logs, I realised that the logs don’t work via preview only via simulator, also the Apple website loads fine and I can interact, but I have no way to go back to the last viewed page once I’ve clicked a link etc – Pulsarman325 Jun 25 '19 at 22:44
  • I added my answer - pay attention to instantiating `WKWebView` in the `UIViewRepresentable`. As I tried to say, this really is the only thing the code you posted isn't doing. Maybe that will hold your last viewed page... it make sense. –  Jun 25 '19 at 23:17

2 Answers2

4

dfd thank you for pointing me on the correct path, your answer needed a little modification because adding the webview as a var to the struct means I needed to set a value on it when I use it, so I also had to add a custom init method. Anyway here is the code that works.

Webview struct

import SwiftUI

import WebKit

struct Webview : UIViewRepresentable {


    let request: URLRequest
    var webview: WKWebView?

    init(web: WKWebView?, req: URLRequest) {
        self.webview = WKWebView()
        self.request = req
    }

    func makeUIView(context: Context) -> WKWebView  {
        return webview!
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        uiView.load(request)
    }

    func goBack(){
        webview?.goBack()
    }

    func goForward(){
        webview?.goForward()
    }
  }

and here is the struct using the webview and adding the buttons to the top

import SwiftUI

struct FullWebView : View {


let webview = Webview(web: nil, req: URLRequest(url: URL(string: "https://www.apple.com")!))

var body: some View {
    VStack{
        HStack{
            Button(action: {
                //do something
                self.webview.goBack()
            }){
                Text("back")
            }

            Spacer()

            Button(action: {
                //do something
                self.webview.goForward()
            }){
                Text("forward")
            }

        }

        webview
    }
}
}
Pulsarman325
  • 214
  • 3
  • 16
2

Here is another way using Combine

import Combine
import SwiftUI
import WebKit

private struct WebView: UIViewRepresentable {
    enum Action {
        case goBack, goForward
    }
    
    let htmlContent: String
    let actionPublisher: any Publisher<Action, Never>
    
    func makeUIView(context: Context) -> WKWebView {
        let view = WKWebView()
        context.coordinator.cancellable = actionPublisher.sink { action in
            view.stopLoading()
            switch action {
            case .goBack:
                if view.canGoBack {
                    view.goBack()
                }
            case .goForward:
                if view.canGoForward {
                    view.goForward()
                }
            }
        }
        loadContent(in: view)
        return view
    }
    
    func updateUIView(_ view: WKWebView, context: Context) {
        
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }
    
    private func loadContent(in view: WKWebView) {
        if let url = URL(string: htmlContent), url.scheme == "https" {
            let req = URLRequest(url: url)
            view.load(req)
        }
    }
    
    class Coordinator: NSObject {
        var cancellable: AnyCancellable?
    }
}

struct FullWebView: View {
    let url: String
    private let actionPublisher = PassthroughSubject<WebView.Action, Never>()   
 
    var body: some View {
        VStack {
            WebView(htmlContent: url, actionPublisher: actionPublisher)
            HStack {
                Button {
                    actionPublisher.send(.goBack)
                } label: {
                    Image(systemName: "arrow.left")
                }
                Spacer()
                Button {
                    actionPublisher.send(.goForward)
                } label: {
                    Image(systemName: "arrow.right")
                }
            }
        }
    }
}
ikroop
  • 96
  • 1
  • 6