WKWebView improvements in iOS 15
Media playback APIs, theme color, async support, downloads and more.
Published: June 17, 2021 Sponsored I want a Press KitWith iOS 15 there are another bunch of great improvements to the WKWebView
. As a reminder, with iOS 14 we got JavaScript sandboxing, better ways to inject JS into pages and more.
Note: Some of the stuff shown bellow actually sneaked in with iOS 14.5 (and even with 14.3).
Because there are once again a lot of new stuff, I won't go into too much details to focus on the overview.
Controlling media playback
There are new APIs that allow you to manipulate audio & video playback on the page. Previously we would have to reach for JavaScript and manually play or pause specific element.
With new methods, you can pause all the media on a page and also control if autoplay is enabled or not.
For example we can pause all media playback like this:
webView.pauseAllMediaPlayback {
// completion
}
And since we have async/await, we can do it like this also:
await webView.pauseAllMediaPlayback()
I won't be showing the await
variants for other methods to keep focus on the APIs.
There's second method tailor made for cases where you don't want any autoplay to happen.
webView.setAllMediaPlaybackSuspended(true) {
// done
}
With setAllMediaPlaybackSuspended
there won't be any unexpected videos playing, even if you refresh the page. Call it with false
to resume the playback.
You can also ask webview for current playback state:
webView.requestMediaPlaybackState { state in
switch state {
case .playing:
print("Playing")
case .suspended:
print("Suspended")
case .paused:
print("Paused")
case .none:
print("🤷")
}
}
Async support
Since we already touched upon async/await
support for the new methods, the older ones were updated as well. So stuff like creating PDF from a page, injecting JavaScript and much more can use the new concurrency APIs to avoid callbacks.
Access website theme color
This is pretty small feature but can be very useful. You can ask the WKWebView
to return you theme color of the website if it exists. You can use this color to customize look of your native UI.
The code to observe themeColor
isn't the prettiest one, but it gets the job done:
private var themeObservation: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
themeObservation = webView.observe(\.themeColor) { [unowned self] webView, _ in
self.view.backgroundColor = webView.themeColor ?? .systemBackground
}
}
For simplicity I am changing the background color of root view
, but of course you can use it anyway you want.
In HTML the theme color is defined like this:
<meta name="theme-color" content="#e95420">
Computed theme color
What if theme color isn't defined for particular website? You can still use the underPageBackgroundColor
on the WKWebView
and observe it in similar manner like with themeColor
.
This property is not yet available in the Xcode 13 beta 1 (for Swift at least). You can also set this property to define the background color of the webView when user scrolls past the content.
Disabling text interaction
There is another WKPreference
option which lets you disable all the standard text interactions on particular webView. This is super useful if you are using webView to "just" play videos. This will help insure that user's don't trigger text selection by accident when trying to press the play button for example.
let preferences = WKPreferences()
preferences.textInteractionEnabled = false
let config = WKWebViewConfiguration()
configuration.preferences = preferences
let webView = WKWebView(frame: .zero, configuration: config)
Automatic HTTPS upgrade
WKWebView
will now automatically upgrade known hosts to HTTPS protocol for security reasons. There is also a configuration flag to disable this behavior in cases it causes problems in your app.
let config = WKWebViewConfiguration()
config.upgradeKnownHostsToHTTPS = false
APIs for camera and microphone
There's new functionality to manage user's permissions to access device's camera and microphone. New properties cameraCaptureState
and microphoneCaptureState
let you easily access the current state.
You can decide when to grant access to this hardware like this:
func webView(_ webView: WKWebView, decideMediaCapturePermissionsFor origin: WKSecurityOrigin, initiatedBy frame: WKFrameInfo, type: WKMediaCaptureType) async -> WKPermissionDecision {
return .grant
}
This will also remove the second prompt that is otherwise present. When page in WKWebView
requests these permissions, the first prompt is related to the app and second one is related to the actual website.
Using this new delegate method, you can allow access to only pages you trust and user doesn't see two prompts.
New class for managing downloads
With WKDownload
you can encapsulate and manage file downloads. There are a multiple ways of initiating download. For example in the decidePolicyFor
method from WKNavigationAction
or WKNavigationResponse
.
You can also start download manually by calling startDownload
on the instance of WKWebView
. This will return an instance of WKDownload
and you need to set its delegate
property otherwise the download won't happen.
There is single required method for the WKDownloadDelegate
:
func download(_ download: WKDownload, decideDestinationUsing response: URLResponse, suggestedFilename: String, completionHandler: @escaping (URL?) -> Void) {
}
Alternatively there is async
variant.
Your job is to return URL there the download should be saved.
If download fails, you can restart it by calling resumeDownload
and passing it the Data
instance this delegate method gave you:
func download(_ download: WKDownload, didFailWithError error: Error, resumeData: Data?) {
// handle download error
}
For more be sure to check out the WWDC 2021 session.
Uses: Xcode 13 & Swift 5.5