Web Inspector for WKWebView (iOS / macOS).
WebInspectorKit: Container UI, pane descriptors, Observation stateWebInspectorKitCore(Core): DOM/Network engines, runtime actors, bundled inspector scripts
WebInspectorKit depends on WebInspectorKitCore.
- DOM tree browsing (element picking, highlights, deletion, attribute editing)
- Network request logging (fetch/XHR/WebSocket) with buffering/active mode switching
- Configurable panes via
WIPaneDescriptor - Explicit lifecycle via
WISessionController(connect(to:),suspend(),disconnect())
- Swift 6.2+
- iOS 18 / macOS 15+
- WKWebView with JavaScript enabled
import UIKit
import WebKit
import WebInspectorKit
final class BrowserViewController: UIViewController {
private let pageWebView = WKWebView(frame: .zero)
private let inspector = WISessionController()
@objc private func presentInspector() {
let container = WIContainerViewController(
inspector,
webView: pageWebView,
tabs: [.dom(), .element(), .network()]
)
container.modalPresentationStyle = .pageSheet
if let sheet = container.sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.selectedDetentIdentifier = .medium
sheet.prefersGrabberVisible = true
}
present(container, animated: true)
}
}import AppKit
import WebKit
import WebInspectorKit
final class BrowserWindowController: NSWindowController {
let pageWebView = WKWebView(frame: .zero)
let inspector = WISessionController()
@objc func presentInspector() {
let container = WIContainerViewController(
inspector,
webView: pageWebView,
tabs: [.dom(), .element(), .network()]
)
let inspectorWindow = NSWindow(contentViewController: container)
inspectorWindow.styleMask = [.titled, .closable, .miniaturizable, .resizable]
inspectorWindow.title = "Web Inspector"
inspectorWindow.setContentSize(NSSize(width: 960, height: 720))
inspectorWindow.makeKeyAndOrderFront(nil)
}
}let customPane = WIPaneDescriptor(
id: "my_custom_pane",
title: "Custom",
systemImage: "folder",
role: .other
) { context in
#if canImport(UIKit)
return UIViewController()
#else
return NSViewController()
#endif
}
let container = WIContainerViewController(
inspector,
webView: pageWebView,
tabs: [.dom(), .element(), .network(), customPane]
)See MIGRATION.md for details on breaking changes.
Run tests with xcodebuild from the repository root. Execute both macOS and iOS Simulator test suites.
# macOS: Package tests (Core)
xcodebuild -workspace WebInspectorKit.xcworkspace \
-scheme WebInspectorKitCoreTests \
-destination 'platform=macOS' \
test
# macOS: Package tests (Feature)
xcodebuild -workspace WebInspectorKit.xcworkspace \
-scheme WebInspectorKitFeatureTests \
-destination 'platform=macOS' \
test
# iOS Simulator: Package tests (Core)
xcodebuild -workspace WebInspectorKit.xcworkspace \
-scheme WebInspectorKitCoreTests \
-destination 'platform=iOS Simulator,name=iPhone 17,OS=latest' \
test
# iOS Simulator: Package tests (Feature)
xcodebuild -workspace WebInspectorKit.xcworkspace \
-scheme WebInspectorKitFeatureTests \
-destination 'platform=iOS Simulator,name=iPhone 17,OS=latest' \
testIf the destination does not exist on your machine, check available simulators with:
xcrun simctl list devices availableRun TypeScript tests (Vitest) from the repository root:
pnpm -s run test:ts
pnpm -s run typecheck:tsSee LICENSE.
