SwiftUI Web View unable to go back and forward

539 Views Asked by At

I am using WebView for loading the html into view . I added the forward and back button to go back and forward with require code but the problem is when I enter the url and click more link , I do not see the back button or forward button is enable ..

Here is the content view ..

import SwiftUI

struct ContentView: View {

    @State private var selection = 0
   
    var body: some View {

        TabView(selection: $selection) {
                NavigationView {
                WebListView().navigationBarTitle("Web View ", displayMode: .inline)
                    .toolbarBackground(Color.white,for: .navigationBar)
                    .toolbarBackground(.visible, for: .navigationBar)
                    .accentColor(.red)
                    .onAppear() {
                        UITabBar.appearance().barTintColor = .white
                    }

            }.tabItem {
                Image(systemName: "person.crop.circle")
                Text("Web View")
            }.tag(2)
        }
    }
}

Here is the code ListView ..

import SwiftUI

struct WebListView: View {
    @StateObject var model = WebViewModel()

    var body: some View {
        WebContentView()
            .font(.system(size: 30, weight: .bold, design: .rounded))
            .toolbar {
                ToolbarItemGroup(placement: .automatic) {
                    Button(action: {
                        model.goBack()
                    }, label: {
                        Image(systemName: "chevron.left")
                    })
                    .disabled(!model.canGoBack)
                    .font(.system(size: 20))

                    Button(action: {
                        model.goForward()
                    }, label: {
                        Image(systemName: "chevron.right")
                    })
                    .disabled(!model.canGoForward)
                    .font(.system(size: 20))

                    Spacer()
                }
            }
       }
}

Code for UIViewRepresentable..

struct WebView: UIViewRepresentable {
    typealias UIViewType = WKWebView

    let webView: WKWebView

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

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

Here is the WebContent view code ..

import Combine
import WebKit
import SwiftUI

@MainActor
struct WebContentView: View {

    @StateObject var model = WebViewModel()

    var body: some View {
        ZStack(alignment: .bottom) {
            Color.blue
            
            VStack(spacing: 0) {
                HStack(spacing: 10) {
                    HStack {
                        TextField("Enter url",
                                  text: $model.urlString)
                        .keyboardType(.URL)
                        .autocapitalization(.none)
                        .disableAutocorrection(true)
                        .padding(8)
                        .font(.system(size: 15))
                        Spacer()
                    }
                    .background(Color.white)
                    .cornerRadius(30)
                    
                    Button("GO", action: {
                        model.loadUrl()
                    })
                    .foregroundColor(.white)
                    .padding(10)
                    .font(.system(size: 15))
                    .background(.blue)
                    
                }.padding(10)
                
                ZStack {
                    WebView(webView: model.webView)
                    if model.isLoading {
                        ProgressView()
                            .progressViewStyle(CircularProgressViewStyle())
                    }
                }
            }
        }
    }
}

struct WebContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Here is the view Model code ..

@MainActor
class WebViewModel: ObservableObject {
    let webView: WKWebView

    private let navigationDelegate: WebViewNavigationDelegate

    init() {
        let configuration = WKWebViewConfiguration()
        configuration.websiteDataStore = .nonPersistent()
        webView = WKWebView(frame: .zero, configuration: configuration)
        navigationDelegate = WebViewNavigationDelegate()

        webView.navigationDelegate = navigationDelegate
        setupBindings()
    }

    @Published var urlString: String = ""
    @Published var canGoBack: Bool = false
    @Published var canGoForward: Bool = false
    @Published var isLoading: Bool = false

    private func setupBindings() {
        webView.publisher(for: \.canGoBack)
            .assign(to: &$canGoBack)

        webView.publisher(for: \.canGoForward)
            .assign(to: &$canGoForward)

        webView.publisher(for: \.isLoading)
            .assign(to: &$isLoading)
    }

    func loadUrl() {

        guard let url = URL(string: urlString) else {
            return
        }
        webView.load(URLRequest(url: url))
    }

    func goForward() {
        webView.goForward()
    }

    func goBack() {
        webView.goBack()
    }
}

here is code for delegate ..

import WebKit

class WebViewNavigationDelegate: NSObject, WKNavigationDelegate {
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        // TODO
        decisionHandler(.allow)
    }

    func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
        // TODO
        decisionHandler(.allow)
    }
}

Here is the screenshot .. enter image description here

1

There are 1 best solutions below

0
On BEST ANSWER

The problem is that you're creating a new WebViewModel in WebContentView

Change

struct WebContentView: View {

    @StateObject var model = WebViewModel()
    //etc
}

to

struct WebContentView: View {

    @EnvironmentObject var model: WebViewModel
    //etc
}

Then update here…

struct WebListView: View {
    @StateObject var model = WebViewModel()

    var body: some View {
        WebContentView()
            .environmentObject(model) //  add environmentObject