CtrlK
BlogDocsLog inGet started
Tessl Logo

dpearson2699/swift-ios-skills

Agent skills for iOS, iPadOS, Swift, SwiftUI, and modern Apple framework development.

71

Quality

89%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Advisory

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

loading-and-observation.mdskills/swiftui-webkit/references/

Loading and Observation

Contents

  • Simple WebView(url:)
  • Controlled WebPage loading
  • Observing progress and title
  • Observing navigation events
  • Ephemeral pages and custom user agents

Simple WebView(url:)

Use WebView(url:) when the app only needs to display a URL and does not need explicit page control.

import SwiftUI
import WebKit

struct MarketingPageView: View {
    let url: URL

    var body: some View {
        WebView(url: url)
            .webViewBackForwardNavigationGestures(.enabled)
    }
}

This is the lowest-friction path for embedded content.

Controlled WebPage loading

Create a WebPage when the app needs to drive loading itself.

@Observable
@MainActor
final class ArticleModel {
    let page = WebPage()
    var lastError: String?

    func load(_ url: URL) async {
        do {
            for try await _ in page.load(URLRequest(url: url)) {
            }
        } catch {
            lastError = error.localizedDescription
        }
    }
}
struct ArticleDetailView: View {
    @State private var model = ArticleModel()
    let url: URL

    var body: some View {
        WebView(model.page)
            .task {
                await model.load(url)
            }
    }
}

You can also load:

  • for try await _ in page.load(url) { }
  • for try await _ in page.load(html: htmlString, baseURL: baseURL) { }
  • for try await _ in page.load(data, mimeType: "text/html", characterEncoding: "utf-8", baseURL: baseURL) { }

Observing progress and title

WebPage is observable, so SwiftUI can bind directly to its state.

struct ReaderView: View {
    @State private var page = WebPage()

    var body: some View {
        WebView(page)
            .navigationTitle(page.title ?? "Loading")
            .overlay(alignment: .top) {
                if page.isLoading {
                    ProgressView(value: page.estimatedProgress)
                        .padding()
                }
            }
            .task {
                do {
                    for try await _ in page.load(URLRequest(url: URL(string: "https://example.com/docs")!)) {
                    }
                } catch {
                    // Handle load failure.
                }
            }
    }
}

Useful properties:

  • title
  • url
  • isLoading
  • estimatedProgress
  • themeColor
  • hasOnlySecureContent
  • backForwardList

Observing navigation events

Use currentNavigationEvent for a lightweight current-state view. Use navigations to observe the full sequence of navigation events.

@MainActor
func observeNavigations(for page: WebPage) {
    Task {
        for await event in page.navigations {
            switch event {
            case .finished:
                print("Navigation finished")
            case .failed(let error), .failedProvisionalNavigation(let error):
                print("Navigation failed: \(error)")
            default:
                break
            }
        }
    }
}

This is the right place to trigger follow-up work like parsing headings after a finished navigation.

Ephemeral pages and custom user agents

Use WebPage.Configuration when you need a nonpersistent page, custom user agent, or tighter loading rules.

@MainActor
func makeMetadataPage() -> WebPage {
    var configuration = WebPage.Configuration()
    configuration.loadsSubresources = false
    configuration.defaultNavigationPreferences.allowsContentJavaScript = false
    configuration.websiteDataStore = .nonPersistent()

    let page = WebPage(configuration: configuration)
    page.customUserAgent = "MetadataBot/1.0"
    return page
}

Use nonpersistent pages when you want an isolated web session or metadata fetch path without shared cookies or long-lived website data.

skills

CHANGELOG.md

README.md

tile.json