Highlight
WKWebView adds JavaScript-free content operation API, automatic HTTPS upgrade, WebRTC permission control and download management, making the experience of the app’s embedded web pages closer to that of a native browser.
Core Content
To display web content in an app, developers face a choice: useSFSafariViewControllerstillWKWebView?The former is simple but has limited interaction, while the latter is flexible but requires writing a lot of JavaScript injection code.
WWDC2021 givesWKWebViewA batch of native APIs have been added. Many things that previously had to be injected into JavaScript can now be done directly by calling Objective-C/Swift methods.This reduces the complexity of native-web bridging and allows security features such as App-Bound Domains to work properly.
At the same time, a number of browser-level features that were originally only available in Safari have also been opened: automatic HTTP upgrade, HTTPS, and WebRTCgetUserMedia, file download.These make the experience of web pages embedded in the app closer to that of a full browser.
Detailed Content
Manipulate web content without JavaScript
There are several problems with injecting JavaScript: native and web interfaces are easy to write messily, may produce side effects, and are incompatible with the App-Bound Domains security feature.Three new APIs are added this year that do not require JavaScript.
Theme Color
Web page passed<meta name="theme-color">Set the theme color and the App can read it directly:
// Read the web page theme color
if let themeColor = webView.themeColor {
headerView.backgroundColor = themeColor
}
// Read the computed background color, useful when themeColor is not set
if let bgColor = webView.underPageBackgroundColor {
scrollView.backgroundColor = bgColor
}
Key points:
themeColorReturn the theme color set by the meta tag of the web page (05:51)underPageBackgroundColoris the calculated background color used for the fill color when scrolling to the edge of the content (06:12)- Safari uses these two attributes to achieve visual integration of web pages and browser chrome (06:22)
- can write
underPageBackgroundColorCustomize scroll edge color (06:28)
Disable text interaction
In video content, users may accidentally touch the text selection control instead of the play button:
let preferences = WKPreferences()
preferences.textInteractionEnabled = false
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
let webView = WKWebView(frame: .zero, configuration: configuration)
Key points:
textInteractionEnabled = falseTurn off all text interaction (07:01)- Text selection, magnifying glass, and copy menus will not appear
- Suitable for web page scenarios focusing on media playback
Media Playback Control
Previously, pausing media required injecting JavaScript to traverse the DOM to find the video/audio element.Now call the API directly:
// Pause all media on the page, equivalent to calling pause() on each media element
webView.pauseAllMediaPlayback()
// Suspend all media playback; newly loaded media will not autoplay either
webView.setAllMediaPlaybackSuspended(true)
// Resume media playback
webView.setAllMediaPlaybackSuspended(false)
// Close all picture-in-picture windows
webView.closeAllMediaPresentations()
// Query media playback state
webView.requestMediaPlaybackState { state in
switch state {
case .playing, .paused, .suspended:
print(state)
@unknown default:
break
}
}
Key points:
pauseAllMediaPlayback()Only affects the media on the current page, new media will still play after refreshing (09:37)setAllMediaPlaybackSuspended(true)It is a WebView level setting that still takes effect after refreshing (10:08)- The latter also disables user media controls until restored (07:57)
- No need to understand web page DOM structure (09:14)
Browser-level functionality
Automatic HTTPS upgrade
Starting with iOS 15 and macOS Monterey, HTTP requests to sites known to support HTTPS will automatically be upgraded to HTTPS (12:04).No need for developers to do anything.
If you need to turn it off (such as local debugging):
let configuration = WKWebViewConfiguration()
configuration.upgradeKnownHostsToHTTPS = false // Not recommended for production
Key points:
- Enabled by default, no configuration required (12:17)
- Only works on sites known to support HTTPS (12:07)
- The shutdown flag should not appear in production code (12:31)
WebRTC / getUserMedia
iOS 14.3 starts WKWebView supportgetUserMedia, further improved this year:
// When loading through a custom scheme handler, the permission prompt shows the app name instead of the website URL
let schemeHandler = MyCustomSchemeHandler()
configuration.setURLSchemeHandler(schemeHandler, forURLScheme: "myapp")
// Control camera and microphone permission prompts
class MyUIDelegate: NSObject, WKUIDelegate {
func webView(
_ webView: WKWebView,
requestMediaCapturePermissionFor origin: WKSecurityOrigin,
initiatedByFrame frame: WKFrameInfo,
type: WKMediaCaptureType,
decisionHandler: @escaping (WKPermissionDecision) -> Void
) {
// Skip the second prompt for trusted domains
if origin.host == "trusted.example.com" {
decisionHandler(.grant)
} else {
decisionHandler(.prompt) // Show the system prompt
}
}
}
webView.uiDelegate = MyUIDelegate()
Key points:
- When the custom scheme handler is loaded, the permission prompt displays the App name instead of the website URL (12:54)
WKUIDelegateNew way to control when prompted for camera/microphone permissions (13:15)- return
.grantdirect authorization,.promptShow prompt,.denyRejection (14:01) - Keep default behavior when not implementing delegate (system prompt) (13:55)
Control media capture status
// Turn off the camera
webView.setCameraCaptureState(.none)
// Turn on the camera
webView.setCameraCaptureState(.active)
// Turn off the microphone
webView.setMicrophoneCaptureState(.none)
// Turn on the microphone
webView.setMicrophoneCaptureState(.active)
Key points:
- Directly control camera and microphone capture status, no JavaScript required (15:04)
- The recording indicator in the status bar will disappear after closing (16:54)
- Indicator reappears after turning on (17:02)
Download management
WKWebView adds download support and three triggering methods:
Web page JavaScript trigger
// JavaScript in the web page
const link = document.createElement('a');
link.href = 'https://example.com/image.jpg';
link.download = 'cute-kitten.jpg';
link.click();
// App-side handling
func webView(
_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
) {
if navigationAction.shouldPerformDownload {
decisionHandler(.download)
return
}
decisionHandler(.allow)
}
Server triggered via Content-Disposition
func webView(
_ webView: WKWebView,
decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void
) {
let headers = navigationResponse.response as? HTTPURLResponse
if let disposition = headers?.value(forHTTPHeaderField: "Content-Disposition"),
disposition.contains("attachment") {
decisionHandler(.download)
return
}
decisionHandler(.allow)
}
Initiated by App
let request = URLRequest(url: URL(string: "https://example.com/file.pdf")!)
webView.startDownload(using: request) { download in
download.delegate = self
}
Download Commission
extension ViewController: WKDownloadDelegate {
func download(
_ download: WKDownload,
decideDestinationUsing response: URLResponse,
suggestedFilename: String,
completionHandler: @escaping (URL?) -> Void
) {
let url = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)[0]
.appendingPathComponent(suggestedFilename)
completionHandler(url)
}
func download(
_ download: WKDownload,
didFailWithError error: Error,
resumeData: Data?
) {
// resumeData can be used to resume the download
if let resumeData = resumeData {
// Save resumeData and resume later
}
}
}
Key points:
- Three triggering methods: web page JS, server header, and App active call (17:54)
WKDownloadThe delegate must be set, otherwise the download will be automatically canceled (19:09)decideDestinationUsingDeciding where to save files (19:13)- on failure
resumeDataCan be used to resume interrupted downloads (19:21)
Core Takeaways
- Prioritize using native API instead of JavaScript injection.
pauseAllMediaPlayback、themeColorThe API makes the code more concise and prevents App-Bound Domains from being disabled. - use
setAllMediaPlaybackSuspendedDo persistent media control.ComparepauseAllMediaPlaybackMore thorough, it still takes effect after refreshing the page, suitable for scenarios that require global muting. - WebRTC permission delegation improves user experience.Directly authorize trusted self-owned domain names to avoid users being disturbed by continuous pop-up windows; keep prompts for other domain names to take into account both security and experience.
- Download management makes content sharing easy.Previously, WKWebView did not support downloading, and users could only take screenshots or long press the image to save it.Now it can fully support file downloads and resume downloads.
- Automatic HTTPS upgrade reduces configuration burden.There is no need to do protocol upgrade logic at the App layer. WebKit automatically handles websites that are known to support HTTPS.
Related Sessions
- Develop advanced web content — New JavaScript/WebAssembly features for Safari and WebKit
- Discover Web Inspector improvements — Web Inspector debugging tool
- Design for Safari 15 — Safari 15 Design Adaptation Guide
Comments
GitHub Issues · utterances