biggreentree
biggreentree

Reputation: 1943

SwiftUI: Cannot show alert with WKWebView and WebView: UIViewRepresentable

I need a way to understand why I cannot show alert messages in swiftui for this code, I'd like to show alert if something happens while loading web page (also monitoring cookies, but is another task) I even tried using a UIAlertController, but with no success. , I think my issue is about alert with is presenting one after the other, but I'm presenting only one. :

import SwiftUI
import WebKit

struct ContentView: View {
    @State private var showWebView = false
    @State private var showError = false
    @State private var errorMessage = ""
    
    var body: some View {
        Button("open WebView") {
            if !showError {
                showWebView = true
            }
        }
        .fullScreenCover(isPresented: $showWebView) {
            if let url = URL(string: "https://www.google.com") {
                WebView(url: url) { error in
                    self.errorMessage = error
                    self.showError = true
                }
            } else {
                Text("not valid url")
            }
        }
        .alert(isPresented: $showError) {
            Alert(title: Text("Error"), message: Text(errorMessage), dismissButton: .default(Text("OK"), action: {}))
        }
    }
}


struct WebView: UIViewRepresentable {
let url: URL
var handleError: ((String) -> Void)?

func makeUIView(context: Context) -> WKWebView {
    let webView = WKWebView()
    webView.navigationDelegate = context.coordinator
    return webView
}

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

func makeCoordinator() -> Coordinator {
    Coordinator(self)
}

//-------------------------------------------------------------------------------------
class Coordinator: NSObject, WKNavigationDelegate {
    var parent: WebView
    
    init(_ parent: WebView) {
        self.parent = parent
    }
    
    func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
        //            self.parent.handleError?(error.localizedDescription)
    }
    
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        self.parent.handleError?("Hello, fake error for testing")
    }
}
//-------------------------------------------------------------------------------------

}

on console I get this messages

2024-02-21 15:48:06.529734+0100 testRedirectWebView[32686:38145275] [Sandbox] Could not enable Mach bootstrap, errno = 1. 2024-02-21 15:48:06.529863+0100 testRedirectWebView[32686:38145275] [Sandbox] Could not enable Mach bootstrap, errno = 1. 2024-02-21 15:48:07.385311+0100 testRedirectWebView[32686:38145275] [Security] This method should not be called on the main thread as it may lead to UI unresponsiveness.

2024-02-21 15:48:07.385440+0100 testRedirectWebView[32686:38145275] [Security] This method should not be called on the main thread as it may lead to UI unresponsiveness.

2024-02-21 15:48:08.063187+0100 testRedirectWebView[32686:38145275] [Presentation] Attempt to present <SwiftUI.PlatformAlertController: 0x103033a00> on <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier_: 0x10301e800> (from <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier_: 0x10301e800>) which is already presenting <TtGC7SwiftUI29PresentationHostingControllerVS_7AnyView: 0x103031e00>.

2024-02-21 15:48:08.225535+0100 testRedirectWebView[32686:38145275] [Security] This method should not be called on the main thread as it may lead to UI unresponsiveness.

2024-02-21 15:48:08.225651+0100 testRedirectWebView[32686:38145275] [Security] This method should not be called on the main thread as it may lead to UI unresponsiveness.

Upvotes: 0

Views: 632

Answers (2)

Andrew Moore
Andrew Moore

Reputation: 82

If you attach the alert to the WebView instead of the Button, the alert displays as expected with no console errors.

.fullScreenCover(isPresented: $showWebView) {
    if let url = URL(string: "https://www.google.com") {
        WebView(url: url) { error in
            self.errorMessage = error
            self.showError = true
        }
        .alert(isPresented: $showError) {
            Alert(title: Text("Error"), message: Text(errorMessage), dismissButton: .default(Text("OK"), action: {}))
        }
    } else {
        Text("not valid url")
    }
}

I believe the error you saw came from the button trying to present the web page and the alert. This way, the button presents the web page, then the web page presents the alert.

Upvotes: 0

MatBuompy
MatBuompy

Reputation: 2093

I don't think you can you can show alerts when using a fullScreenCover. You should use an overlay maybe and expand it to cover the whole screen using a GeometryReader:

struct ContentView: View {
    @State private var showWebView = false
    @State private var showError = false
    @State private var errorMessage = ""
    
    var body: some View {
        GeometryReader { proxy in
            let size = proxy.size
            VStack {
                Button("open WebView") {
                    if !showError {
                        showWebView = true
                    }
                }
                .overlay(alignment: .center) {
                    if showWebView {
                        if let url = URL(string: "https://www.google.com") {
                            WebView(url: url) { error in
                                self.errorMessage = error
                                self.showError = true
                            }
                            .frame(width: size.width, height: size.height)
                            
                        } else {
                            Text("not valid url")
                        }
                    }
                }
                .alert(isPresented: $showError) {
                    Alert(title: Text("Error"), message: Text(errorMessage), dismissButton: .default(Text("OK"), action: {}))
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .ignoresSafeArea()
    }
}

This way your alerts can be presented. Also, I've had no signs so far of the error you mentioned.

Upvotes: 0

Related Questions