# Loading and observing pages

Part of the `swiftui-webkit` skill ([../SKILL.md](../SKILL.md)). All code
needs iOS 26, iPadOS 26, macOS 26 or visionOS 26 and `import WebKit`.

## The URL-only view

When all you need is to show a page and let people move back and forth,
`WebView(url:)` plus the gesture modifier is the least code:

```swift
struct PolicyPage: View {
    let link: URL

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

## Loading through a WebPage

A model that owns the page can keep the last failure for the UI:

```swift
@Observable
@MainActor
final class HelpCenterModel {
    let page = WebPage()
    var failure: Error?

    func show(_ address: URL) async {
        failure = nil
        do {
            for try await _ in page.load(URLRequest(url: address)) { }
        } catch {
            failure = error
        }
    }
}
```

The other load entry points return the same kind of event sequence:

```swift
_ = page.load(address)
_ = page.load(html: "<h1>Offline</h1><p>Try again later.</p>")   // baseURL defaults to about:blank
_ = page.load(markupData,
              mimeType: "text/html",
              characterEncoding: .utf8,
              baseURL: bundleFolder)
```

Iterate the returned sequence (or ignore it) depending on whether the caller
cares when the load ends. `characterEncoding` takes a `String.Encoding`, not a
string like `"utf-8"`.

## Progress, title and other state

Besides `title`, `url`, `isLoading`, `estimatedProgress` and
`backForwardList`, a page reports `themeColor` (the page's theme colour, if
any) and `hasOnlySecureContent`.

```swift
struct DocsPane: View {
    @State private var model = HelpCenterModel()
    let home: URL

    var body: some View {
        WebView(model.page)
            .navigationTitle(model.page.title)
            .toolbarBackground(model.page.themeColor ?? .clear, for: .navigationBar)
            .overlay(alignment: .top) {
                if model.page.isLoading {
                    ProgressView(value: model.page.estimatedProgress)
                        .padding(.horizontal)
                }
            }
            .task { await model.show(home) }
    }
}
```

## Following navigation events

`load` returns the events of that one navigation; `page.navigations` delivers
the events of every navigation the page makes, including link taps. Both are
throwing sequences. The event cases are `.startedProvisionalNavigation`,
`.receivedServerRedirect`, `.committed` and `.finished`. Failures are not
events: they arrive as thrown errors, typically a `WebPage.NavigationError`
such as `.failedProvisionalNavigation(_:)`, `.webContentProcessTerminated`
or `.pageClosed`.

```swift
@MainActor
func watch(_ page: WebPage) async {
    do {
        for try await event in page.navigations {
            switch event {
            case .finished:
                await collectHeadings(from: page)   // follow-up work goes here
            default:
                break
            }
        }
    } catch WebPage.NavigationError.failedProvisionalNavigation(let reason) {
        report(reason)
    } catch {
        report(error)
    }
}
```

The finished event is the right moment for work that needs the loaded
document, such as reading its headings. For lightweight UI, the observable
properties (`isLoading`, `estimatedProgress`) are usually enough; there is no
separate "current event" property.

## Throwaway pages and custom user agents

For a quick metadata fetch or an isolated session, configure a page that
keeps nothing around:

```swift
@MainActor
func makeScratchPage() -> WebPage {
    var setup = WebPage.Configuration()
    setup.loadsSubresources = false
    setup.defaultNavigationPreferences.allowsContentJavaScript = false
    setup.websiteDataStore = .nonPersistent()

    let page = WebPage(configuration: setup)
    page.customUserAgent = "ExampleReader/2.1"
    return page
}
```

A non-persistent data store means no shared cookies and no long-lived website
data, so each such page is its own sealed session.
