Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI: WKWebView not loading

Tags:

swiftui

I have a WKWebView as UIViewRepresentable in my SwiftUI application and it works initially. The webpage is loaded and I can navigate to other pages. If I force the web view to load another page nothing happens. It worked with Xcode 11.3 but seems to be broken since 11.4. Here is an example. Reload forces to load page 1 or 2 by random. Any suggestion what might be the problem?

import WebKit
import SwiftUI

struct WebView: UIViewRepresentable{

    var url: URL

    let urls = [URL(string: "https://google.com/")!, URL(string: "https://bing.com")!]

    private let webview = WKWebView()

    fileprivate func loadRequest() {

        let index = Int(Date().timeIntervalSince1970) % 2
        print(index)

        let request = URLRequest(url: urls[index])
        webview.load(request)
    }

    func makeUIView(context: UIViewRepresentableContext<WebView>) -> WKWebView {

        loadRequest()

        return webview
    }

    func updateUIView(_ uiView: WKWebView, context: UIViewRepresentableContext<WebView>) {}

    func reload(){
        loadRequest()
    }

}

The WebView ist used this way (simplified example):

 var body: some View {
        VStack{
            Button(action: {
                self.webView.reload()
            }){
                Text("reload")
            }
            webView
        }
}

Things are bit more complicated. The reloading is triggered by an action sheet and there seems to be a problem as well. Please see my simplified example with an action sheet:

@State private var showingSheet = false
 @State private var shouldRefresh = false
    var body: some View {

            ZStack{

                WebView(url: url, reload: $shouldRefresh)
                    .padding(paddingEdgeInsets())
                    .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
                    .edgesIgnoringSafeArea(.all)

                Button(action: {
                    self.showingSheet = true
                }){
                    Text("Show sheet")
                }



            }.actionSheet(isPresented: $showingSheet) {
                ActionSheet(title: Text("OPTIONS"), buttons: [
                    .default(Text("RELOAD"), action: {
                        self.shouldRefresh = true
                    }), .default(Text("EXIT"), action: {
                        self.presentationMode.wrappedValue.dismiss()
                    }), .cancel()])
            }
        }
like image 213
netshark1000 Avatar asked Dec 10 '25 21:12

netshark1000


1 Answers

You try to apply 'reference'-base approach to value based SwiftUI views. It is not allowed, so does not work. In your usage code webView in button action and in below are different independent values (because WebView is struct).

So here is possible approach. Tested with Xcode 11.4 / iOS 13.4

demo

struct TestWebView: View {
    @State private var shouldRefresh = false
    var body: some View {
        VStack{
            Button(action: {
                self.shouldRefresh = true
            }){
                Text("Reload")
            }
            WebView(url: nil, reload: $shouldRefresh)
        }
    }
}


struct WebView: UIViewRepresentable{

    var url: URL?     // optional, if absent, one of below search servers used
    @Binding var reload: Bool

    private let urls = [URL(string: "https://google.com/")!, URL(string: "https://bing.com")!]
    private let webview = WKWebView()

    fileprivate func loadRequest(in webView: WKWebView) {
        if let url = url {
            webView.load(URLRequest(url: url))
        } else {
            let index = Int(Date().timeIntervalSince1970) % 2
            webView.load(URLRequest(url: urls[index]))
        }
    }

    func makeUIView(context: UIViewRepresentableContext<WebView>) -> WKWebView {
        loadRequest(in: webview)
        return webview
    }

    func updateUIView(_ uiView: WKWebView, context: UIViewRepresentableContext<WebView>) {
        if reload {
            loadRequest(in: uiView)
            DispatchQueue.main.async {
                self.reload = false     // must be async
            }
        }
    }
}
like image 126
Asperi Avatar answered Dec 13 '25 15:12

Asperi