iOS 10 Link Preview API in WKWebView

Multi-Touch gestures have always been an integral part of the iPhone experience, but pinch and double tap were just the beginning of the story. Last year iPhone 6s introduced an entirely new dimension to the Multi-Touch experience with 3D Touch. 3D Touch-capable displays sense how much pressure a user applies when she touches the screen. Using this technology, iOS has a new Peek and Pop experience that, among other things, lets users preview content in Safari. I’m happy to announce some updates that allow WKWebView developers to take advantage of this too.

Peek and Pop

WebKit supports Peek and Pop as an easy way to preview links. Apps built against the iOS 10 SDK will have Peek and Pop link preview enabled by default, but the feature is not new; since iOS 9, WKWebView clients could opt into Peek-based link previews on 3D Touch-capable devices using the allowsLinkPreview property on WKWebView. When the allowsLinkPreview property is set to true, users can gently press on links to peek them, which loads the link in another view over the app and blurs the app in the background.

WKPedia

If the user presses harder on the link, it pops open in Safari.

Not all apps are the same

This behavior works great for apps like Mail. Mail uses WebKit to display rich HTML content, but it is not a web browser, so link navigation is always handed over to Safari. However, many WebKit apps do handle link navigation and behave more like web browsers themselves. In iOS 10 we have made Peek and Pop work better for these types of apps.

Consider WKPedia. Anders and I made WKPedia, a Wikipedia browser app, as a demo app for WKWebView in iOS 8. WKPedia has a WKNavigationDelegate that enforces a navigation policy so that when a user taps links to other Wikipedia pages, WKPedia handles the link navigation within the app, but when the user taps a link hosted on any other domain, WKPedia hands the navigation off to Safari.

I have set allowsLinkPreview to true in order to enable Peek and Pop, but the default behavior is not ideal for this app. Now if a user taps on a link, WKPedia uses the load policy to decide whether to navigate to the link inside the app or to hand it over to Safari. But if she peeks and pops the link, the pop will always navigate to the link in Safari regardless of the load policy.

Customizing Peek and Pop in iOS 10

If you have an app like WKPedia and are looking to provide a better experience, you can start using a new API we’ve added in iOS 10! The new API will display a custom view controller whenever the user peeks and pops a link using 3D Touch inside a WKWebView. It consists of three new delegate methods in the WKUIDelegate protocol.

optional func webView(_ webView: WKWebView,  shouldPreviewElement elementInfo: WKPreviewElementInfo) -> Bool
optional func webView(_ webView: WKWebView,  previewingViewControllerForElement elementInfo: WKPreviewElementInfo,  defaultActions previewActions: [WKPreviewActionItem]) -> UIViewController?
optional func webView(_ webView: WKWebView,  commitPreviewingViewController previewingViewController: UIViewController)

The first method, webView(_:shouldPreviewElement:) will be invoked as soon as the user touches an element. Returning false will disable previews entirely for this element and prevent the other methods from being invoked. Returning true will give you the opportunity to provide a custom view controller if the user touches with enough force to initiate a Peek.

If the user does enter a Peek, webView(_:previewingViewControllerForElement:defaultActions:) is your opportunity to provide that custom view controller. Returning any non-nil view controller will result in that view controller being displayed as a Peek preview. The defaultActions parameter is an array of the actions that WebKit would use as previewActionItems for this element by default. If you wish to use any of these actions, you just need to return them from your view controller’s implementation of previewActionItems. Here’s WKPedia’s implementation of this delegate method:

func webView(_ webView: WKWebView, previewingViewControllerForElement elementInfo: WKPreviewElementInfo, defaultActions previewActions: [WKPreviewActionItem]) -> UIViewController? {
    // If this URL is not allowed by our navigation policy, then it must not be a link
    // to a page on Wikipedia, so returning nil will let WebKit display its default
    // preview which will pop to Safari.
    if !navigationAllowedForURL(elementInfo.linkURL) {
        return nil;
    }

    let previewViewController = ArticlePreviewController()

    previewViewController.view.frame = self.view.frame
    previewViewController.view.backgroundColor = UIColor.white()

    var actions = [WKPreviewActionItem]()
    for previewAction in previewActions {
        if previewAction.identifier == WKPreviewActionItemIdentifierShare {
            actions.append(previewAction)
        }
    }
    previewViewController.defaultPreviewActionItems = actions

    previewViewController.load(request: URLRequest(url:elementInfo.linkURL))

    return previewViewController
}

Finally, webView(_:commitPreviewingViewController:) will be invoked if the user touches with enough force to Pop the view controller. At that time, you can choose to display the popped view controller inside your app.

This API gives developers control of Peek and Pop so that users can have a better experience. I look forward to seeing it in more apps!

For more information, contact me at @dethbakin, or Jonathan Davis, Apple’s Web Technologies Evangelist, at @jonathandavis.