<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>swiftui open url &#8211; swiftyplace</title>
	<atom:link href="https://www.swiftyplace.com/blog/tag/swiftui-open-url/feed" rel="self" type="application/rss+xml" />
	<link>https://www.swiftyplace.com</link>
	<description>Learn how to build amazing apps with SwiftUI and Combine</description>
	<lastBuildDate>Wed, 18 Oct 2023 16:47:21 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://www.swiftyplace.com/wp-content/uploads/2023/08/cropped-logo-1-32x32.png</url>
	<title>swiftui open url &#8211; swiftyplace</title>
	<link>https://www.swiftyplace.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>How to Load a SwiftUI WebView with WKWebView</title>
		<link>https://www.swiftyplace.com/blog/loading-a-web-view-in-swiftui-with-wkwebview?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=loading-a-web-view-in-swiftui-with-wkwebview</link>
					<comments>https://www.swiftyplace.com/blog/loading-a-web-view-in-swiftui-with-wkwebview#comments</comments>
		
		<dc:creator><![CDATA[Karin Prater]]></dc:creator>
		<pubDate>Tue, 16 May 2023 08:27:03 +0000</pubDate>
				<category><![CDATA[SwiftUI]]></category>
		<category><![CDATA[SwiftUI Components]]></category>
		<category><![CDATA[swiftui open url]]></category>
		<category><![CDATA[swiftui web]]></category>
		<category><![CDATA[swiftui web view]]></category>
		<category><![CDATA[swiftui webview]]></category>
		<category><![CDATA[webview swift]]></category>
		<category><![CDATA[wkwebview]]></category>
		<guid isPermaLink="false">https://swiftyplace.com/?p=799</guid>

					<description><![CDATA[<p>Discover how to create a SwiftUI WebView using WKWebView from UIKit/AppKit in Swift. Build an in-app browser.</p>
<p>The post <a rel="nofollow" href="https://www.swiftyplace.com/blog/loading-a-web-view-in-swiftui-with-wkwebview">How to Load a SwiftUI WebView with WKWebView</a> appeared first on <a rel="nofollow" href="https://www.swiftyplace.com">swiftyplace</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>You oftentimes want to show web content within an app. For example, to show terms and conditions or show a tutorial site for more help</p>



<p>Unfortunately, there is no SwiftUI WebView component. However, SwiftUI offers a Link button that will open the provided URL in the Safari browser. If you want to keep your users inside the app, you can use WKWebView from UIKit/Appkit and integrate it with SwiftUI using UIViewRepresentable and NSViewRepresentable.</p>



<p>In this blog post, we&#8217;ll delve into how to create a WebView in SwiftUI.</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b07.png" alt="⬇" class="wp-smiley" style="height: 1em; max-height: 1em;" /> download the project from Github&nbsp;<a href="https://github.com/gahntpo/WebViewProject" target="_blank" rel="noopener">https://github.com/gahntpo/Web&#8230;</a></p>



<div style="height:49px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Opening a web page in Safari with SwiftUI Link view</h2>



<p>In SwiftUI, the <strong>Link</strong> view provides an interactive element that opens a URL in Safari when clicked. Here&#8217;s how you can use it:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="import SwiftUI

struct ContentView: View {
    var body: some View {
         Link(&quot;Visit SwiftyPlace&quot;,
               destination: URL(string: &quot;https://www.swiftyplace.com&quot;)!)
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">struct</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">ContentView</span><span style="color: #F6F6F4">: View {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> body: </span><span style="color: #F286C4">some</span><span style="color: #F6F6F4"> View {</span></span>
<span class="line"><span style="color: #F6F6F4">         </span><span style="color: #97E1F1">Link</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Visit SwiftyPlace</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">               </span><span style="color: #97E1F1">destination</span><span style="color: #F6F6F4">: </span><span style="color: #97E1F1">URL</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">string</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">https://www.swiftyplace.com</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span><span style="color: #F286C4">!</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div style="height:22px" aria-hidden="true" class="wp-block-spacer"></div>



<p>The user can go back to your app by tapping your app name in the top left edge. Your user will leave the app. This is not the best experience.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://www.swiftyplace.com/wp-content/uploads/2023/08/swiftui_Link_button-1024x939.jpg" alt="SwiftUI example with Link button to open an url in safari"/></figure>
</div>


<div style="height:49px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">What is the WebKit framework?</h2>



<p>Apple&#8217;s WebKit framework is a versatile tool, powering not only Safari, but also extending its capabilities to developers. Central to this is WebView, provided by WebKit&#8217;s WKWebView class. This allows developers to display a variety of web content directly within their apps and interact with JavaScript from Swift or Objective-C. While not all web technologies found in a full desktop browser are supported, it opens up myriad possibilities for integrating interactive web content into your apps. Let&#8217;s explore how to leverage these tools for your SwiftUI apps.</p>



<div style="height:49px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">How to write a simple SwiftUI WebView SwiftUI component?</h2>



<p>Once you&#8217;ve set up your project, it&#8217;s time to integrate WKWebView with SwiftUI. Let&#8217;s create a new SwiftUI View that represents a web link:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
    let url: URL
    
    func makeUIView(context: Context) -&gt; WKWebView  {
        let wkwebView = WKWebView()
        let request = URLRequest(url: url)
        wkwebView.load(request)
        return wkwebView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">SwiftUI</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">WebKit</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">struct</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">WebView</span><span style="color: #F6F6F4">: UIViewRepresentable {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> url: URL</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">makeUIView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884; font-style: italic">context</span><span style="color: #F6F6F4">: Context) </span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4"> WKWebView  {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> wkwebView </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">WKWebView</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> request </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">URLRequest</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">url</span><span style="color: #F6F6F4">: url)</span></span>
<span class="line"><span style="color: #F6F6F4">        wkwebView.</span><span style="color: #97E1F1">load</span><span style="color: #F6F6F4">(request)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> wkwebView</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">updateUIView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884">_</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">uiView</span><span style="color: #F6F6F4">: WKWebView, </span><span style="color: #62E884; font-style: italic">context</span><span style="color: #F6F6F4">: Context) {</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div style="height:22px" aria-hidden="true" class="wp-block-spacer"></div>



<p>In the above code, I declare a struct called WebView that conforms to the <strong>UIViewRepresentable</strong> protocol. This protocol lets you create a SwiftUI view based on a UIKit view.</p>



<p>The <strong>makeUIView(:)</strong> function is where we create an instance of <strong>WKWebView</strong>. It creates a URL request, and load the website.</p>



<p>The <strong>updateUIView(:)</strong> function is required by the <strong>UIViewRepresentable</strong> protocol and is used to update the UIView as needed. In this case, we don&#8217;t need to do anything when the view updates, so we leave the function empty.</p>



<div style="height:22px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="gb-container gb-container-c5dedc2e">
<div class="gb-container gb-container-f7a433c7">
<div class="gb-container gb-container-7a8dc7a3">

<figure class="gb-block-image gb-block-image-f04fbbad"><a href="https://www.swiftyplace.com/free-swiftui-layout-cookbook" target="_blank" rel="noopener noreferrer"><img fetchpriority="high" decoding="async" width="640" height="500" class="gb-image gb-image-f04fbbad" src="http://www.swiftyplace.com/wp-content/uploads/2024/01/swiftui_roadmap_preview.webp" alt="swiftui roadmap " title="swiftui_roadmap_preview" srcset="https://www.swiftyplace.com/wp-content/uploads/2024/01/swiftui_roadmap_preview.webp 640w, https://www.swiftyplace.com/wp-content/uploads/2024/01/swiftui_roadmap_preview-300x234.webp 300w" sizes="(max-width: 640px) 100vw, 640px" /></a></figure>

</div>

<div class="gb-container gb-container-98352fe6">

<h2 class="gb-headline gb-headline-4aacd94c gb-headline-text">Feeling Lost in SwiftUI?</h2>



<p class="gb-headline gb-headline-5e6c4f85 gb-headline-text">This SwiftUI roadmap shows you what to learn next.</p>



<ul style="margin-top:0;margin-bottom:0;font-size:18px" class="wp-block-list">
<li>Key concepts at a glance</li>



<li>Spot your knowledge gaps</li>



<li>Guide your learning path</li>
</ul>


<div class="gb-container gb-container-34a8ad02">

<a class="gb-button gb-button-63e90e76 gb-button-blue" href="https://school.swiftyplace.com/f/swiftui-roadmap" target="_blank" rel="noopener noreferrer"><span class="gb-button-text">Get the FREE PDF</span><span class="gb-icon"><svg aria-hidden="true" role="img" height="1em" width="1em" viewBox="0 0 256 512" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z"></path></svg></span></a>

</div>


<p class="gb-headline gb-headline-7ef80e86 gb-headline-text">Ideal for beginners and self-learners.</p>

</div>
</div>
</div>


<div style="height:60px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Using the WebView</h2>



<p>Now we can use the WebView in our SwiftUI views. In your <strong>ContentView</strong>, you can use the WebView like this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="import SwiftUI
struct ContentView: View {
    var body: some View {
        WebView(url: URL(string: &quot;https://www.swiftyplace.com&quot;)!)
            .edgesIgnoringSafeArea(.all)
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">SwiftUI</span></span>
<span class="line"><span style="color: #F286C4">struct</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">ContentView</span><span style="color: #F6F6F4">: View {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> body: </span><span style="color: #F286C4">some</span><span style="color: #F6F6F4"> View {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">WebView</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">url</span><span style="color: #F6F6F4">: </span><span style="color: #97E1F1">URL</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">string</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">https://www.swiftyplace.com</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span><span style="color: #F286C4">!</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">            .</span><span style="color: #97E1F1">edgesIgnoringSafeArea</span><span style="color: #F6F6F4">(.all)</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div style="height:19px" aria-hidden="true" class="wp-block-spacer"></div>



<p>In the <strong>ContentView</strong>, I created a WebView that loads &#8220;<a href="https://www.swiftyplace.com/" target="_blank" rel="noopener noreferrer"><strong><u>https://www.swiftyplace.com</u></strong></a>&#8220;. I use <strong>edgesIgnoringSafeArea(.all)</strong>to make the WebView fill the entire screen.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://s3.us-east-1.amazonaws.com/contents.newzenler.com/17130/library/swiftui_webview6463b482a3853_lg.jpg" alt="SwiftUI webview that fills the entire screen"/></figure>
</div>


<div style="height:19px" aria-hidden="true" class="wp-block-spacer"></div>



<p>The content in the web view is scrollable by default. The webview will expand to fill in the evaluable space. If you want to use a web view inside a scroll view, you have to add a frame to set a specific size. Otherwise, the web view will shrink to its minimum height which is zero. In the following, you can see a basic example.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="import SwiftUI

struct ContentView: View {
    var body: some View {
      ScrollView {
        WebView(url: URL(string: &quot;https://www.swiftyplace.com&quot;)!)
          .frame(height: 300)
      }
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">struct</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">ContentView</span><span style="color: #F6F6F4">: View {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> body: </span><span style="color: #F286C4">some</span><span style="color: #F6F6F4"> View {</span></span>
<span class="line"><span style="color: #F6F6F4">      ScrollView {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">WebView</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">url</span><span style="color: #F6F6F4">: </span><span style="color: #97E1F1">URL</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">string</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">https://www.swiftyplace.com</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span><span style="color: #F286C4">!</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">          .</span><span style="color: #97E1F1">frame</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">height</span><span style="color: #F6F6F4">: </span><span style="color: #BF9EEE">300</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">      }</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div style="height:48px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Adding an Activity Indicator to a WebView in SwiftUI</h2>



<p>A common requirement when loading a webpage in a WebView is to display an activity indicator while the page is loading. This provides a clear visual cue to the user that something is happening. Let&#8217;s walk through how to implement this in SwiftUI.</p>



<div style="height:36px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">Updating the WebView</h3>



<p>You need to modify our <strong>WebView</strong> to show an activity indicator while loading. We&#8217;ll add a <strong>Coordinator</strong> class to handle the <strong>WKNavigationDelegate</strong> callbacks, specifically <strong>webView(_:didStartProvisionalNavigation:)</strong> and <strong>webView(_:didFinish:)</strong>. We&#8217;ll also add a <strong>@State</strong> property <strong>isLoading</strong> to track whether the page is loading.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
    let url: URL
    @Binding var isLoading: Bool
    func makeCoordinator() -&gt; Coordinator {
        Coordinator(self)
    }
    func makeUIView(context: Context) -&gt; WKWebView  {
        let wkwebView = WKWebView()
        wkwebView = context.coordinator
        wkwebView.load(URLRequest(url: url))
        return wkwebView
    }
    func updateUIView(_ uiView: WKWebView, context: Context) {
    }

    class Coordinator: NSObject, WKNavigationDelegate {
        var parent: WebView
        init(_ parent: WebView) {
            self.parent = parent
        }
        func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
            parent.isLoading = true
        }
        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            parent.isLoading = false
        }
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">SwiftUI</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">WebKit</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">struct</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">WebView</span><span style="color: #F6F6F4">: UIViewRepresentable {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> url: URL</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@Binding</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> isLoading: </span><span style="color: #97E1F1; font-style: italic">Bool</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">makeCoordinator</span><span style="color: #F6F6F4">() </span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4"> Coordinator {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">Coordinator</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE; font-style: italic">self</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">makeUIView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884; font-style: italic">context</span><span style="color: #F6F6F4">: Context) </span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4"> WKWebView  {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> wkwebView </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">WKWebView</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">        wkwebView </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> context.coordinator</span></span>
<span class="line"><span style="color: #F6F6F4">        wkwebView.</span><span style="color: #97E1F1">load</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">URLRequest</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">url</span><span style="color: #F6F6F4">: url))</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> wkwebView</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">updateUIView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884">_</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">uiView</span><span style="color: #F6F6F4">: WKWebView, </span><span style="color: #62E884; font-style: italic">context</span><span style="color: #F6F6F4">: Context) {</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Coordinator</span><span style="color: #F6F6F4">: NSObject, WKNavigationDelegate {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> parent: WebView</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">init</span><span style="color: #F6F6F4">(</span><span style="color: #62E884">_</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">parent</span><span style="color: #F6F6F4">: WebView) {</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #BF9EEE; font-style: italic">self</span><span style="color: #F6F6F4">.parent </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> parent</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">webView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884">_</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">webView</span><span style="color: #F6F6F4">: WKWebView, </span><span style="color: #62E884">didStartProvisionalNavigation</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">navigation</span><span style="color: #F6F6F4">: WKNavigation</span><span style="color: #F286C4">!</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">            parent.isLoading </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">true</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">webView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884">_</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">webView</span><span style="color: #F6F6F4">: WKWebView, </span><span style="color: #62E884">didFinish</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">navigation</span><span style="color: #F6F6F4">: WKNavigation</span><span style="color: #F286C4">!</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">            parent.isLoading </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">false</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div style="height:21px" aria-hidden="true" class="wp-block-spacer"></div>



<p>In the <strong>Coordinator</strong>, we set <strong>isLoading</strong> to true when the page starts loading and set it to false when it finishes loading.</p>



<div style="height:53px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Using the WebView with Loading Indicator</h2>



<p>Finally, let&#8217;s create a new reusable component in SwiftUI ´<strong>LoadingWebView</strong>´ to use the <strong>WebView</strong> and show the <strong>ProgressView</strong> while the page is loading:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="import SwiftUI

struct LoadingWebView: View {

    @State private var isLoading = true
    let url: URL?
    
    var body: some View {
        ZStack {
          if let url = url {
            WebView(url: url, isLoading: $isLoading)
                .edgesIgnoringSafeArea(.all)
            if isLoading {
                ProgressView()
            }
          }
        }
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">struct</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">LoadingWebView</span><span style="color: #F6F6F4">: View {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@State</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> isLoading </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">true</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> url: URL</span><span style="color: #F286C4">?</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> body: </span><span style="color: #F286C4">some</span><span style="color: #F6F6F4"> View {</span></span>
<span class="line"><span style="color: #F6F6F4">        ZStack {</span></span>
<span class="line"><span style="color: #F6F6F4">          </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> url </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> url {</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #97E1F1">WebView</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">url</span><span style="color: #F6F6F4">: url, </span><span style="color: #97E1F1">isLoading</span><span style="color: #F6F6F4">: $isLoading)</span></span>
<span class="line"><span style="color: #F6F6F4">                .</span><span style="color: #97E1F1">edgesIgnoringSafeArea</span><span style="color: #F6F6F4">(.all)</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> isLoading {</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">ProgressView</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">            }</span></span>
<span class="line"><span style="color: #F6F6F4">          }</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div style="height:21px" aria-hidden="true" class="wp-block-spacer"></div>



<p>The <strong>ZStack</strong> allows us to overlay the <strong>ProgressView</strong> on top of the <strong>web view</strong>. The <strong>ProgressView</strong> is only visible when <strong>isLoading</strong> is true.</p>



<div style="height:38px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">How to show an error message when the web view cannot be opened?</h3>



<p>If an invalid url is used, the current implementation will keep on showing the loading indicator. Instead, I want to get the error from the navigation delegate and show it to the user. First I have to add a binding for an error value:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="struct WebView: UIViewRepresentable {
    let url: URL
    @Binding var isLoading: Bool
    @Binding var error: Error?
    ...
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">struct</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">WebView</span><span style="color: #F6F6F4">: UIViewRepresentable {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> url: URL</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@Binding</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> isLoading: </span><span style="color: #97E1F1; font-style: italic">Bool</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@Binding</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> error: </span><span style="color: #97E1F1; font-style: italic">Error</span><span style="color: #F286C4">?</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">...</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div style="height:21px" aria-hidden="true" class="wp-block-spacer"></div>



<p>That I am going to update from within the web view coordinator and use the delegate callback webview(didFailProvisionalNavigation:, withError) to update the isLoading to false and set the error:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="struct WebView: UIViewRepresentable {
    ...
    class Coordinator: NSObject, WKNavigationDelegate {
        ...
        func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
            parent.isLoading = true
        }
        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            parent.isLoading = false
        }
        func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
            print(&quot;loading error: \(error)&quot;)
            parent.isLoading = false
            parent.error = error
        }
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">struct</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">WebView</span><span style="color: #F6F6F4">: UIViewRepresentable {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">...</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Coordinator</span><span style="color: #F6F6F4">: NSObject, WKNavigationDelegate {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">...</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">webView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884">_</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">webView</span><span style="color: #F6F6F4">: WKWebView, </span><span style="color: #62E884">didStartProvisionalNavigation</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">navigation</span><span style="color: #F6F6F4">: WKNavigation</span><span style="color: #F286C4">!</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">            parent.isLoading </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">true</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">webView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884">_</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">webView</span><span style="color: #F6F6F4">: WKWebView, </span><span style="color: #62E884">didFinish</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">navigation</span><span style="color: #F6F6F4">: WKNavigation</span><span style="color: #F286C4">!</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">            parent.isLoading </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">false</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">webView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884">_</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">webView</span><span style="color: #F6F6F4">: WKWebView, </span><span style="color: #62E884">didFailProvisionalNavigation</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">navigation</span><span style="color: #F6F6F4">: WKNavigation</span><span style="color: #F286C4">!</span><span style="color: #F6F6F4">, </span><span style="color: #62E884">withError</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">error</span><span style="color: #F6F6F4">: </span><span style="color: #97E1F1; font-style: italic">Error</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #97E1F1">print</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">loading error: </span><span style="color: #F286C4">\(</span><span style="color: #E7EE98">error</span><span style="color: #F286C4">)</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">            parent.isLoading </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">false</span></span>
<span class="line"><span style="color: #F6F6F4">            parent.</span><span style="color: #BF9EEE">error</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> error</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div style="height:21px" aria-hidden="true" class="wp-block-spacer"></div>



<p>Now I finally have all the information to make an advanced webview in Swiftui:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="struct LoadingWebView: View {
    @State private var isLoading = true
    @State private var error: Error? = nil
    let url: URL?

    var body: some View {
        ZStack {
            if let error = error {
                Text(error.localizedDescription)
                    .foregroundColor(.pink)
            } else if let url = url {
                PlatformIndependentWebView(url: url,
                                           isLoading: $isLoading,
                                           error: $error)
                     .edgesIgnoringSafeArea(.all)
                if isLoading {
                    ProgressView()
                }
            } else {
                Text(&quot;Sorry, we could not load this url.&quot;)
            }

        }
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">struct</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">LoadingWebView</span><span style="color: #F6F6F4">: View {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@State</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> isLoading </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">true</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@State</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> error: </span><span style="color: #97E1F1; font-style: italic">Error</span><span style="color: #F286C4">?</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">nil</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> url: URL</span><span style="color: #F286C4">?</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> body: </span><span style="color: #F286C4">some</span><span style="color: #F6F6F4"> View {</span></span>
<span class="line"><span style="color: #F6F6F4">        ZStack {</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> error </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> error {</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">Text</span><span style="color: #F6F6F4">(error.localizedDescription)</span></span>
<span class="line"><span style="color: #F6F6F4">                    .</span><span style="color: #97E1F1">foregroundColor</span><span style="color: #F6F6F4">(.pink)</span></span>
<span class="line"><span style="color: #F6F6F4">            } </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> url </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> url {</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">PlatformIndependentWebView</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">url</span><span style="color: #F6F6F4">: url,</span></span>
<span class="line"><span style="color: #F6F6F4">                                           </span><span style="color: #97E1F1">isLoading</span><span style="color: #F6F6F4">: $isLoading,</span></span>
<span class="line"><span style="color: #F6F6F4">                                           </span><span style="color: #97E1F1">error</span><span style="color: #F6F6F4">: $error)</span></span>
<span class="line"><span style="color: #F6F6F4">                     .</span><span style="color: #97E1F1">edgesIgnoringSafeArea</span><span style="color: #F6F6F4">(.all)</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> isLoading {</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #97E1F1">ProgressView</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">                }</span></span>
<span class="line"><span style="color: #F6F6F4">            } </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">Text</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Sorry, we could not load this url.</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div style="height:21px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image"><img decoding="async" src="https://www.swiftyplace.com/wp-content/uploads/2023/08/swiftui_webview_loading_indicator_error-1024x676.jpg" alt="Swiftui example with a webview that has a loading state and error state"/></figure>



<div style="height:54px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">How to open a WebView on macOS?</h2>



<p>If you tried to use the current version of web view on macOS, you would get an error saying &#8220;cannot find UIViewRepresentable&#8221;. On macOS you have to use NSViewRepresentable instead. Because I don&#8217;t want to copy past the same implementation from the iOS app, I am going to add this protocol conformance conditionally. First, I am making web view more generic by adding NSViewRepresantable and UIViewRepresentable in extensions:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="#if os(macOS)
extension WebView: NSViewRepresentable {
    func makeNSView(context: Context) -&gt; WKWebView {
        makeWebView(context: context)
    }
    func updateNSView(_ nsView: WKWebView, context: Context) {

    }
}
#else
extension WebView: UIViewRepresentable {
     func makeUIView(context: Context) -&gt; WKWebView {
         makeWebView(context: context)
     }

     func updateUIView(_ uiView: WKWebView, context: Context) {
     }
}
#endif" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">#</span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">os</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE">macOS</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F286C4">extension</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">WebView</span><span style="color: #F6F6F4">: NSViewRepresentable {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">makeNSView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884; font-style: italic">context</span><span style="color: #F6F6F4">: Context) </span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4"> WKWebView {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">makeWebView</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">context</span><span style="color: #F6F6F4">: context)</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">updateNSView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884">_</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">nsView</span><span style="color: #F6F6F4">: WKWebView, </span><span style="color: #62E884; font-style: italic">context</span><span style="color: #F6F6F4">: Context) {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"><span style="color: #F6F6F4">#</span><span style="color: #F286C4">else</span></span>
<span class="line"><span style="color: #F286C4">extension</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">WebView</span><span style="color: #F6F6F4">: UIViewRepresentable {</span></span>
<span class="line"><span style="color: #F6F6F4">     </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">makeUIView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884; font-style: italic">context</span><span style="color: #F6F6F4">: Context) </span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4"> WKWebView {</span></span>
<span class="line"><span style="color: #F6F6F4">         </span><span style="color: #97E1F1">makeWebView</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">context</span><span style="color: #F6F6F4">: context)</span></span>
<span class="line"><span style="color: #F6F6F4">     }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">     </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">updateUIView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884">_</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">uiView</span><span style="color: #F6F6F4">: WKWebView, </span><span style="color: #62E884; font-style: italic">context</span><span style="color: #F6F6F4">: Context) {</span></span>
<span class="line"><span style="color: #F6F6F4">     }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"><span style="color: #F6F6F4">#</span><span style="color: #F286C4">endif</span></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>The shared code that I use to set up the web view in makeUIView/makeNSView is now separate:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="struct WebView {
    var url: URL
    @Binding var isLoading: Bool
    @Binding var error: Error?

    func makeCoordinator() -&gt; WebView.Coordinator {
         Coordinator(self)
     }

    func makeWebView(context: Context) -&gt; WKWebView {
        let webView = WKWebView()
        webView.navigationDelegate = context.coordinator
        let request = URLRequest(url: url, cachePolicy: .returnCacheDataElseLoad)
        webView.load(request)
        return webView
    }

    class Coordinator : NSObject, WKNavigationDelegate {
        var parent: PlatformIndependentWebView

        init(_ uiWebView: PlatformIndependentWebView) {
            self.parent = uiWebView
        }

        ...
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">struct</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">WebView</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> url: URL</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@Binding</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> isLoading: </span><span style="color: #97E1F1; font-style: italic">Bool</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@Binding</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> error: </span><span style="color: #97E1F1; font-style: italic">Error</span><span style="color: #F286C4">?</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">makeCoordinator</span><span style="color: #F6F6F4">() </span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4"> WebView.Coordinator {</span></span>
<span class="line"><span style="color: #F6F6F4">         </span><span style="color: #97E1F1">Coordinator</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE; font-style: italic">self</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">     }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">makeWebView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884; font-style: italic">context</span><span style="color: #F6F6F4">: Context) </span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4"> WKWebView {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> webView </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">WKWebView</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">        webView.navigationDelegate </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> context.coordinator</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> request </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">URLRequest</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">url</span><span style="color: #F6F6F4">: url, </span><span style="color: #97E1F1">cachePolicy</span><span style="color: #F6F6F4">: .returnCacheDataElseLoad)</span></span>
<span class="line"><span style="color: #F6F6F4">        webView.</span><span style="color: #97E1F1">load</span><span style="color: #F6F6F4">(request)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> webView</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Coordinator</span><span style="color: #F6F6F4"> : NSObject, WKNavigationDelegate {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> parent: PlatformIndependentWebView</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">init</span><span style="color: #F6F6F4">(</span><span style="color: #62E884">_</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">uiWebView</span><span style="color: #F6F6F4">: PlatformIndependentWebView) {</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #BF9EEE; font-style: italic">self</span><span style="color: #F6F6F4">.parent </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> uiWebView</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">...</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="gb-container gb-container-c5dedc2e">
<div class="gb-container gb-container-f7a433c7">
<div class="gb-container gb-container-7a8dc7a3">

<figure class="gb-block-image gb-block-image-f04fbbad"><a href="https://www.swiftyplace.com/free-swiftui-layout-cookbook" target="_blank" rel="noopener noreferrer"><img fetchpriority="high" decoding="async" width="640" height="500" class="gb-image gb-image-f04fbbad" src="http://www.swiftyplace.com/wp-content/uploads/2024/01/swiftui_roadmap_preview.webp" alt="swiftui roadmap " title="swiftui_roadmap_preview" srcset="https://www.swiftyplace.com/wp-content/uploads/2024/01/swiftui_roadmap_preview.webp 640w, https://www.swiftyplace.com/wp-content/uploads/2024/01/swiftui_roadmap_preview-300x234.webp 300w" sizes="(max-width: 640px) 100vw, 640px" /></a></figure>

</div>

<div class="gb-container gb-container-98352fe6">

<h2 class="gb-headline gb-headline-4aacd94c gb-headline-text">Feeling Lost in SwiftUI?</h2>



<p class="gb-headline gb-headline-5e6c4f85 gb-headline-text">This SwiftUI roadmap shows you what to learn next.</p>



<ul style="margin-top:0;margin-bottom:0;font-size:18px" class="wp-block-list">
<li>Key concepts at a glance</li>



<li>Spot your knowledge gaps</li>



<li>Guide your learning path</li>
</ul>


<div class="gb-container gb-container-34a8ad02">

<a class="gb-button gb-button-63e90e76 gb-button-blue" href="https://school.swiftyplace.com/f/swiftui-roadmap" target="_blank" rel="noopener noreferrer"><span class="gb-button-text">Get the FREE PDF</span><span class="gb-icon"><svg aria-hidden="true" role="img" height="1em" width="1em" viewBox="0 0 256 512" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z"></path></svg></span></a>

</div>


<p class="gb-headline gb-headline-7ef80e86 gb-headline-text">Ideal for beginners and self-learners.</p>

</div>
</div>
</div>


<div style="height:21px" aria-hidden="true" class="wp-block-spacer"></div>



<p>Don&#8217;t forget to change your app&#8217;s requirements to allow network requests otherwise, your web view will not be able to work.</p>



<figure class="wp-block-image"><img decoding="async" src="https://www.swiftyplace.com/wp-content/uploads/2023/08/swiftui_webview_macos_sandbox-1024x743.jpg" alt="macOS sandbox settings to allow loading a web page."/><figcaption class="wp-element-caption">MacOS sandbox settings to allow loading a web page.</figcaption></figure>



<div style="height:54px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">SwiftUI WebView Example: Showing Web View inside a Sheet</h2>



<p>As an example, I want to show a web view inside a sheet. The following is a simple example:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="struct SheetWebView: View {
    @State private var isSheetPresented = false
    @State private var isLoading = true
    let url = URL(string: &quot;https://www.swiftyplace.com&quot;)

    var body: some View {
        Button(action: {
            isSheetPresented = true
        }) {
            Text(&quot;Open Web Page&quot;)
        }
        .sheet(isPresented: $isSheetPresented) {
            VStack(spacing: 0) {
                #if os(macOS)
                HStack {
                    Text(url?.absoluteString ?? &quot;&quot;)
                    Spacer()
                    Button {
                        isSheetPresented.toggle()
                    } label: {
                        Label(&quot;Close&quot;, systemImage: &quot;xmark.circle&quot;)
                            .labelStyle(.iconOnly)
                    }
                }
                .padding(10)
                #endif
                LoadingWebView(url: url)
                    .frame(minWidth: 300, minHeight: 300)
            }
        }
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">struct</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">SheetWebView</span><span style="color: #F6F6F4">: View {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@State</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> isSheetPresented </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">false</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@State</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> isLoading </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">true</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> url </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">URL</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">string</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">https://www.swiftyplace.com</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> body: </span><span style="color: #F286C4">some</span><span style="color: #F6F6F4"> View {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #97E1F1">Button</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">action</span><span style="color: #F6F6F4">: {</span></span>
<span class="line"><span style="color: #F6F6F4">            isSheetPresented </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">true</span></span>
<span class="line"><span style="color: #F6F6F4">        }) {</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #97E1F1">Text</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Open Web Page</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">        .</span><span style="color: #97E1F1">sheet</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">isPresented</span><span style="color: #F6F6F4">: $isSheetPresented) {</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #97E1F1">VStack</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">spacing</span><span style="color: #F6F6F4">: </span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">                #</span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">os</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE">macOS</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">                HStack {</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #97E1F1">Text</span><span style="color: #F6F6F4">(url</span><span style="color: #F286C4">?</span><span style="color: #F6F6F4">.absoluteString </span><span style="color: #F286C4">??</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #97E1F1">Spacer</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">                    Button {</span></span>
<span class="line"><span style="color: #F6F6F4">                        isSheetPresented.</span><span style="color: #97E1F1">toggle</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">                    } label</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">                        </span><span style="color: #97E1F1">Label</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Close</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #97E1F1">systemImage</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">xmark.circle</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">                            .</span><span style="color: #97E1F1">labelStyle</span><span style="color: #F6F6F4">(.iconOnly)</span></span>
<span class="line"><span style="color: #F6F6F4">                    }</span></span>
<span class="line"><span style="color: #F6F6F4">                }</span></span>
<span class="line"><span style="color: #F6F6F4">                .</span><span style="color: #97E1F1">padding</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE">10</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">                #</span><span style="color: #F286C4">endif</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">LoadingWebView</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">url</span><span style="color: #F6F6F4">: url)</span></span>
<span class="line"><span style="color: #F6F6F4">                    .</span><span style="color: #97E1F1">frame</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">minWidth</span><span style="color: #F6F6F4">: </span><span style="color: #BF9EEE">300</span><span style="color: #F6F6F4">, </span><span style="color: #97E1F1">minHeight</span><span style="color: #F6F6F4">: </span><span style="color: #BF9EEE">300</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">            }</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div style="height:21px" aria-hidden="true" class="wp-block-spacer"></div>



<p>On iOS, you can dismiss the sheet by swiping it down. But on macOS, I need to provide a button that dismisses the sheet. Additionally on macOS, when the sheet opens it only shows the progress view and is very small. When the website is loaded, it is shown in the same small sheet. Therefore I added a frame of 300 by 300. I used min values to allow the user to drag the sheet larger. You can learn more about sheet in SwiftUI in this <a href="https://www.swiftyplace.com/blog/swiftui-sheets-modals-bottom-sheets-fullscreen-presentation-in-ios" target="_blank" rel="noopener">blog post</a>.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://www.swiftyplace.com/wp-content/uploads/2023/08/swiftui_webview_macos_sheet.jpg" alt="swiftui webviel on macOS"/></figure>
</div>


<div style="height:51px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Making an in-app browser</h2>



<p>If you want to create an in-app browser, you need to deal with more than just displaying a <strong>WebView</strong>. You&#8217;ll also want to add controls for navigation such as back, forward, reload, and possibly an address bar. Here is a simplified example of how you might create an in-app browser with SwiftUI:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="struct BrowserView: View {

    @StateObject var browserViewModel = BrowserViewModel()
    
    var body: some View {
        VStack {
            HStack {
                Button(action: {
                    browserViewModel.goBack()
                }) {
                    Image(systemName: &quot;chevron.backward&quot;)
                }
                .disabled(!browserViewModel.canGoBack)

                Button(action: {
                    browserViewModel.goForward()
                }) {
                    Image(systemName: &quot;chevron.forward&quot;)
                }
                .disabled(!browserViewModel.canGoForward)

                .padding(.trailing, 5)

                TextField(&quot;URL&quot;, text: $browserViewModel.urlString, onCommit: {
                     browserViewModel.loadURLString()
                 })
                 .textFieldStyle(RoundedBorderTextFieldStyle())

                Button(action: {
                    browserViewModel.reload()
                }) {
                    Image(systemName: &quot;arrow.clockwise&quot;)
                }
            }
            .padding(.horizontal)

            if let url =  URL(string: browserViewModel.urlString) {
                BrowserWebView(url: url,
                               viewModel: browserViewModel)
                .edgesIgnoringSafeArea(.all)
            } else {
                Text(&quot;Please, enter a url.&quot;)
            }
        }
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">struct</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">BrowserView</span><span style="color: #F6F6F4">: View {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@StateObject</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> browserViewModel </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">BrowserViewModel</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> body: </span><span style="color: #F286C4">some</span><span style="color: #F6F6F4"> View {</span></span>
<span class="line"><span style="color: #F6F6F4">        VStack {</span></span>
<span class="line"><span style="color: #F6F6F4">            HStack {</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">Button</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">action</span><span style="color: #F6F6F4">: {</span></span>
<span class="line"><span style="color: #F6F6F4">                    browserViewModel.</span><span style="color: #97E1F1">goBack</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">                }) {</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #97E1F1">Image</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">systemName</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">chevron.backward</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">                }</span></span>
<span class="line"><span style="color: #F6F6F4">                .</span><span style="color: #97E1F1">disabled</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">!</span><span style="color: #F6F6F4">browserViewModel.canGoBack)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">Button</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">action</span><span style="color: #F6F6F4">: {</span></span>
<span class="line"><span style="color: #F6F6F4">                    browserViewModel.</span><span style="color: #97E1F1">goForward</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">                }) {</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #97E1F1">Image</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">systemName</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">chevron.forward</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">                }</span></span>
<span class="line"><span style="color: #F6F6F4">                .</span><span style="color: #97E1F1">disabled</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">!</span><span style="color: #F6F6F4">browserViewModel.canGoForward)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">                .</span><span style="color: #97E1F1">padding</span><span style="color: #F6F6F4">(.trailing, </span><span style="color: #BF9EEE">5</span><span style="color: #F6F6F4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">TextField</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">URL</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #97E1F1">text</span><span style="color: #F6F6F4">: $browserViewModel.urlString, </span><span style="color: #97E1F1">onCommit</span><span style="color: #F6F6F4">: {</span></span>
<span class="line"><span style="color: #F6F6F4">                     browserViewModel.</span><span style="color: #97E1F1">loadURLString</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">                 })</span></span>
<span class="line"><span style="color: #F6F6F4">                 .</span><span style="color: #97E1F1">textFieldStyle</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">RoundedBorderTextFieldStyle</span><span style="color: #F6F6F4">())</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">Button</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">action</span><span style="color: #F6F6F4">: {</span></span>
<span class="line"><span style="color: #F6F6F4">                    browserViewModel.</span><span style="color: #97E1F1">reload</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">                }) {</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #97E1F1">Image</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">systemName</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">arrow.clockwise</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">                }</span></span>
<span class="line"><span style="color: #F6F6F4">            }</span></span>
<span class="line"><span style="color: #F6F6F4">            .</span><span style="color: #97E1F1">padding</span><span style="color: #F6F6F4">(.horizontal)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> url </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4">  </span><span style="color: #97E1F1">URL</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">string</span><span style="color: #F6F6F4">: browserViewModel.urlString) {</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">BrowserWebView</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">url</span><span style="color: #F6F6F4">: url,</span></span>
<span class="line"><span style="color: #F6F6F4">                               </span><span style="color: #97E1F1">viewModel</span><span style="color: #F6F6F4">: browserViewModel)</span></span>
<span class="line"><span style="color: #F6F6F4">                .</span><span style="color: #97E1F1">edgesIgnoringSafeArea</span><span style="color: #F6F6F4">(.all)</span></span>
<span class="line"><span style="color: #F6F6F4">            } </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #97E1F1">Text</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Please, enter a url.</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">            }</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div style="height:21px" aria-hidden="true" class="wp-block-spacer"></div>



<p>The textfield shows the current URL string and updates correctly when the user navigates to another web page.</p>



<p>The SwiftUI wrapper around WKWebView is very simple:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="struct BrowserWebView: UIViewRepresentable {
    let url: URL
    @ObservedObject var viewModel: BrowserViewModel
    func makeUIView(context: Context) -&gt; WKWebView {
        let webView = WKWebView()
        viewModel.webView = webView
        webView.load(URLRequest(url: url))
        return webView
    }
    func updateUIView(_ uiView: WKWebView, context: Context) {
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">struct</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">BrowserWebView</span><span style="color: #F6F6F4">: UIViewRepresentable {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> url: URL</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@ObservedObject</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> viewModel: BrowserViewModel</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">makeUIView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884; font-style: italic">context</span><span style="color: #F6F6F4">: Context) </span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4"> WKWebView {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> webView </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">WKWebView</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">        viewModel.webView </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> webView</span></span>
<span class="line"><span style="color: #F6F6F4">        webView.</span><span style="color: #97E1F1">load</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">URLRequest</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">url</span><span style="color: #F6F6F4">: url))</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> webView</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">updateUIView</span><span style="color: #F6F6F4">(</span><span style="color: #62E884">_</span><span style="color: #F6F6F4"> </span><span style="color: #FFB86C; font-style: italic">uiView</span><span style="color: #F6F6F4">: WKWebView, </span><span style="color: #62E884; font-style: italic">context</span><span style="color: #F6F6F4">: Context) {</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div style="height:21px" aria-hidden="true" class="wp-block-spacer"></div>



<p>The handling of forward and backward actions is done by the view model:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono-NL.ttf" style="font-size:1rem;font-family:Code-Pro-JetBrains-Mono-NL,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" data-code="import Foundation
import WebKit

class BrowserViewModel: NSObject, ObservableObject {
    weak var webView: WKWebView? {
        didSet {
            webView?.navigationDelegate = self
        }
    }

    @Published var urlString = &quot;https://www.apple.com&quot;
    @Published var canGoBack = false
    @Published var canGoForward = false

    func loadURLString() {
        if let url = URL(string: urlString) {
            webView?.load(URLRequest(url: url))
        }
    }

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

    func goForward() {
        webView?.goForward()
    }

    func reload() {
        webView?.reload()
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Foundation</span></span>
<span class="line"><span style="color: #F286C4">import</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">WebKit</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">BrowserViewModel</span><span style="color: #F6F6F4">: NSObject, ObservableObject {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">weak</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> webView: WKWebView</span><span style="color: #F286C4">?</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">didSet</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">            webView</span><span style="color: #F286C4">?</span><span style="color: #F6F6F4">.navigationDelegate </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE; font-style: italic">self</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@Published</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> urlString </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">https://www.apple.com</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@Published</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> canGoBack </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">false</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@Published</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> canGoForward </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">false</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">loadURLString</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">let</span><span style="color: #F6F6F4"> url </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">URL</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">string</span><span style="color: #F6F6F4">: urlString) {</span></span>
<span class="line"><span style="color: #F6F6F4">            webView</span><span style="color: #F286C4">?</span><span style="color: #F6F6F4">.</span><span style="color: #97E1F1">load</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">URLRequest</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1">url</span><span style="color: #F6F6F4">: url))</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">goBack</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        webView</span><span style="color: #F286C4">?</span><span style="color: #F6F6F4">.</span><span style="color: #97E1F1">goBack</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">goForward</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        webView</span><span style="color: #F286C4">?</span><span style="color: #F6F6F4">.</span><span style="color: #97E1F1">goForward</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">func</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">reload</span><span style="color: #F6F6F4">() {</span></span>
<span class="line"><span style="color: #F6F6F4">        webView</span><span style="color: #F286C4">?</span><span style="color: #F6F6F4">.</span><span style="color: #97E1F1">reload</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div style="height:21px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image"><img decoding="async" src="https://www.swiftyplace.com/wp-content/uploads/2023/08/swiftui_webview_browser-1024x996.jpg" alt="Swiftui example to use WKWebview to build an app for web browsing."/><figcaption class="wp-element-caption">You can use WKWebview to build an app for web browsing.</figcaption></figure>



<div style="height:43px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Conclusion</h2>



<p>SwiftUI doesn&#8217;t directly support WebView, but by leveraging the WebKit framework, we can create a WebView within the SwiftUI app. This post explored the step-by-step process of creating a WebView in SwiftUI, handling errors, displaying a loading indicator, and implementing web browsers. We also discussed how to make a cross-platform WebView for iOS and macOS. This comprehensive guide should be helpful for developers looking to embed web content directly into their SwiftUI apps, providing a seamless user experience.</p>



<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">FAQ</h2>



<h4 class="wp-block-heading">Does SwiftUI have WebView?</h4>



<p>SwiftUI native views do not have a WebView. However, you can use the WKWebView from WebKit to display web content in a SwiftUI app by using the <strong>UIViewRepresentable</strong> protocol to create a SwiftUI view that wraps the WKWebView.</p>



<h4 class="wp-block-heading">Is WKWebView same as Safari?</h4>



<p>WKWebView is not exactly the same as Safari. WKWebView is a component provided by WebKit which underlies Safari. It allows developers to embed web content in their applications. Safari is a full-featured web browser that uses WebKit to render web pages, but it also includes additional features like bookmarks, history, and sharing features that are not part of WKWebView.</p>



<h4 class="wp-block-heading">What is the usage of WKWebView?</h4>



<p>WKWebView is used to display web content within a native app. It can load and display a wide variety of web content, including HTML, CSS, SVG, images, and JavaScript. It also allows developers to interact with JavaScript code, and manage navigation and loading resources, enabling the creation of custom, in-app browsers or the display of certain web pages without leaving the app.</p>



<h4 class="wp-block-heading">How do I open web view in iOS?</h4>



<p>To open a WebView in iOS, you typically use the WKWebView class from the WebKit framework. After making an instance of WKWebView, you can load web content by creating a URLRequest and calling the load() method. If you&#8217;re using SwiftUI, you&#8217;ll need to wrap the WKWebView in a SwiftUI view using the <strong>UIViewRepresentable</strong> protocol.</p>
<p>The post <a rel="nofollow" href="https://www.swiftyplace.com/blog/loading-a-web-view-in-swiftui-with-wkwebview">How to Load a SwiftUI WebView with WKWebView</a> appeared first on <a rel="nofollow" href="https://www.swiftyplace.com">swiftyplace</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.swiftyplace.com/blog/loading-a-web-view-in-swiftui-with-wkwebview/feed</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: www.swiftyplace.com @ 2026-04-13 17:12:03 by W3 Total Cache
-->