iOS 18, XCode 16, SwiftUI display HTML resource file

So I have decided to write a Swift application... It is going to be a calculator app, and right now I have a version where the calculator itself works and looks nice on the iPhone 15 Pro and iPhone16 simulator, the only thing that is still sort of wobbly is the built-in user manual. I have decided to put the localized manuals as html files into the app, so using the app does not need any external resources and may be used in environments where network access is not expected to be available.

The first version I did of this was to do this:

struct ManualView: View {
    var body: some View {
        if let fileUrl: URL = Bundle.main.url(forResource: "Manual", withExtension: "html") {
            if let nsAttributedString = try? NSAttributedString(url: fileUrl, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil),
                let attributedString = try? AttributedString(nsAttributedString, including: \.uiKit) {
                    Text(attributedString)
            } else {
                Text("<div>Helpfile missing.</div>")
            }
        }
    }
}

#Preview {
	ManualView()
}

The result is extremely ugly. So I was looking for other ways and stumbled upon WKWebView. Now after fiddling around a bit with this I managed to get the preview to display the file view, but when I try to integrate it into the overall UI and open it on the device, the view stays blank. I have seen a few tutorials in the web, but nothing really worked, and occasionally the preview even crashes for reasons that are not quite clear to me. I do not really understand how to correctly integrate this WKWebView into a surrounding ScrollView, when all I want to do is to display a static HTML text without any external links, javascript or other dynamic behaviour. What I have now is this:

struct ManualView: View {

	var body: some View {
		ManualViewRep()
	}
}

struct ManualViewRep: UIViewRepresentable {		
	func updateUIView(_ uiView: WKWebView, context: Context) {
		if let fileUrl: URL = Bundle.main.url(forResource: "Manual", withExtension: "html") {
			uiView.loadFileURL(fileUrl, allowingReadAccessTo: fileUrl)
		}
	}
	
	func makeUIView(context: Context) -> WKWebView {
			let webConfiguration = WKWebViewConfiguration()
			let webView = WKWebView(frame: .zero, configuration: webConfiguration)
		return webView
	}
}

#Preview {
	ManualView()
}

This works in the direct preview in xcode, but everything else stays blank. What am I missing? Loading the file directly in makeUIView does not change anything, adding a default text in an else branch in case fileUrl fails neither. I looked at some web resources, but can't really figure out how this WKUIDelegate and ViewController stuff is supposed to work within the SwiftUI framework, in case that those are needed. Are they? Is there a simple but complete demonstration of how to load an display a HTML resource anywhere? Is there a better approach than WKWebKit to achieve a good result with iOS >= 15?

Answered by asgalon in 809077022

Solved it. It turns out, WKWebKit not only does not need to be wrapped in a ScrollView, it apparently does not work in a ScrollView, and (more or less) does not work only when wrapped in a ScrollView. I put every other tab in a ScrollView before, so did the same with this one. When I simply add it in a VStack instead, it displays fine.

Accepted Answer

Solved it. It turns out, WKWebKit not only does not need to be wrapped in a ScrollView, it apparently does not work in a ScrollView, and (more or less) does not work only when wrapped in a ScrollView. I put every other tab in a ScrollView before, so did the same with this one. When I simply add it in a VStack instead, it displays fine.

Hi @asgalon, I think that could not be necessary to wrap WkWebView in none of SwiftUI container view. WkWebView is itself a subclass of UIScrollView UIKit class, then put it inside a SwiftUI ScrollView probably breaks some layout constraints used during UI rendering. But layout should work as expected without use of SwiftUI container view. Obviously, if you want display it on navigation stack or something else, you should.

If you don't need to use custom configuration on WKWebView, I suggest to use an UIViewRepresentable as follow

struct WebView: UIViewRepresentable {
    
    let url: URL

    func makeUIView(context: Context) -> WKWebView {

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

        let request = URLRequest(url: url)
        webView.load(request)
    }
}

Bye Rob

iOS 18, XCode 16, SwiftUI display HTML resource file
 
 
Q