Search code examples
swiftswiftuiwkwebview

Sheet and alert keeps refreshing my page for WKWebView SwiftUI


I am currently using WKWebView and I added a sheet and an alert inside my wkwebview. However, every time I close the sheet or close the alarm, My WkWebView keeps navigating(loading) back to the origin domain that I specified (for this case, google.com). I an wondering how I can fix this issue.

WebView

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)
    }
    
}

Main View

struct MainView: View {
   var webView: WebView = WebView(request: URLRequest(url: URL(string: "https://www.google.com")!))

 var body: some View {
  VStack(){
   AlertView()
   webView
   SheetScreenView(webView: $webView)
  }
 }

AlertView()

...
  Button(action: {
  }, label: {}..alert(isPresented: $isBookmarked) {
                Alert(title: Text(" \(webView.getURL())"), dismissButton: .default(Text("Ok")))
            }
...

SheetScreenView

....
@State private var showingSheet = false

    var body: some View {
        Button("Show Sheet") {
            showingSheet.toggle()
        }
        .sheet(isPresented: $showingSheet) {
            SheetView()
        }
    }
...

SheetView

struct SheetView: View {
    @Environment(\.presentationMode) var presentationMode

    var body: some View {
        Button("Press to dismiss") {
            presentationMode.wrappedValue.dismiss()
        }
        .font(.title)
        .padding()
        .background(Color.black)
    }
}

Solution

  • Your issue can be reproduced even without the sheet and alert by just rotating the device. The problem is that in updateView (which is going to get called often), you call load on the URLRequest. This is going to get compounded by the fact that you're storing a view in a var which is going to get recreated on every new render of MainView, since Views in SwiftUI are transient.

    The simplest way to avoid this if your URL isn't going to change is to just call load in makeUIView:

    struct WebView : UIViewRepresentable {
        var request: URLRequest
        
        func makeUIView(context: Context) -> WKWebView  {
            let webView = WKWebView()
            webView.load(request)
            return webView
        }
        
        func updateUIView(_ uiView: WKWebView, context: Context) {
            
        }
    }
    
    struct ContentView: View {
        var body: some View {
          WebView(request: URLRequest(url:URL(string: "https://www.google.com")!))
        }
    }
    
    

    If your URL may change, you need a way to compare the previous state with the new one. This seems like the shortest (but certainly not only) way to do this:

    struct WebView : UIViewRepresentable {
        var url: URL
        
        @State private var prevURL : URL?
        
        func makeUIView(context: Context) -> WKWebView  {
            return WKWebView()
        }
        
        func updateUIView(_ uiView: WKWebView, context: Context) {
            if (prevURL != url) {
                let request = URLRequest(url: url)
                uiView.load(request)
                DispatchQueue.main.async { prevURL = url }
            }
        }
    }
    
    struct ContentView: View {
        var body: some View {
            WebView(url: URL(string: "https://www.google.com")!)
        }
    }