Many applications use
WKWebView as a convenient way to display websites without requiring users to leave the app, referred to as in-app browsing. Although this can provide a great user experience, the powerful features available to developers using
WKWebView allow a hosting app to monitor users across all of the sites they visit within the app.
For iOS 14.0 and iPadOS 14.0, we want to make it possible for developers to continue offering an in-app browsing experience without exposing users to tracking risks. Today we are introducing App-Bound Domains, a new, opt-in
WKWebView technology to improve in-app browsing by offering greater privacy to users.
The App-Bound Domains feature takes steps to preserve user privacy by limiting the domains on which an app can utilize powerful APIs to track users during in-app browsing. Applications that opt-in to this new feature can specify up to 10 “app-bound” domains using a new Info.plist key —
WKAppBoundDomains. Note that content supplied by the app through local files, data URLs, and HTML strings are always treated as app bound domains, and do not need to be listed.
<plist version="1.0"> <dict> <key>WKAppBoundDomains</key> <array> <string>example1.com</string> <string>example2.org</string> ... </array> </dict>
WKAppBoundDomains key is added to the Info.plist, all
WKWebView can set the
limitsNavigationsToAppBoundDomains flag in their WKWebView configuration, like so:
webViewConfiguration.limitsNavigationsToAppBoundDomains = YES;
Setting this flag indicates to WebKit that a
WKWebView will only navigate to app-bound domains. Once set, any attempt to navigate away from an app-bound domain will fail with the error: “App-bound domain failure.” A web view which has this configuration flag and loads an app-bound domain from the
WKAppBoundDomains list, or from local resources like file URLs, data URLs, and strings, will have access to the following APIs:
Additionally, an application will have access to the following
WKHTTPCookieStore APIs for accessing cookies for app-bound domains:
(void)setCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void))completionHandler;
(void)getAllCookies:(void (^)(NSArray<NSHTTPCookie *> *))completionHandler;
WKWebView instances are prevented from using these APIs, since they are capable of leaking private data. This makes a
WKWebView navigating to domains outside of the small set of app-bound domains work more like
SafariViewController, which has built-in privacy protections like this already.
We will talk more about specific examples and benefits of App-Bound Domains below.
Example Use Cases
We will use five examples to illustrate the ways App-Bound Domains can be adopted for different types of applications:
- UnchangedApp, an application which does not opt-in to App-Bound Domains.
- ShopApp, an application with only self-hosted content.
- SocialApp, an application with an in-app browser.
- BrowserApp, a full web browser application.
- HybridApp, an application with both self-hosted content and an in-app browser.
First let’s consider UnchangedApp, which does not opt-in to App-Bound Domains or change its behavior in any way. UnchangedApp will experience pre-iOS 14.0
WKWebView behavior, with no restricted APIs on any domains. However, the decision to not adopt App-Bound Domains in UnchangedApp could expose its users to tracking risks, for example if UnchangedApp includes third party code that surreptitiously injects script into web views.
ShopApp (self-hosted content)
Let’s look at a simple example of an application, ShopApp, which only serves web content from its own domain, shop.example. ShopApp can opt-in to App-Bound Domains by creating a new array entry in its Info.plist with the key
WKAppBoundDomains. This kind of app can add up to 10 “app-bound” domains as strings in the array. This might be an example of ShopApp’s Info.plist entry:
<plist version="1.0"> <dict> <key>WKAppBoundDomains</key> <array> <string>shop.example</string> <string>shop-cdn.example</string> </array> </dict>
In order for a
WKWebView to use restricted APIs on these domains, ShopApp will also have to initialize the
WKWebView with the following configuration argument:
webViewConfiguration.limitsNavigationsToAppBoundDomains = YES;
ShopApp will now have access to the full spectrum of APIs listed above when browsing on shop.example and shop-cdn.example. Note that the check for app-bound domains only occurs for the top-level frame, so ShopApp will still be able to display third party iframes from domains outside the app-bound set on shop.example.
SocialApp (with in-app browser)
Now let’s consider a social media application, SocialApp, which is used largely as an in-app browser. A SocialApp user might navigate to many different websites using the app, possibly encountering a tracker, tracker.example, during in-app browsing.
Without the protections offered by App-Bound Domains, it is possible that SocialApp is intrusively using in-app browsing to track users by communicating with tracker.example.
If the developers of SocialApp want a better user privacy experience they have two paths forward:
WKWebViewfor in-app browsing.
SafariViewControllerprotects user data from SocialApp by loading pages outside of SocialApp’s process space. SocialApp can guarantee it is giving its users the best available user privacy experience while using SafariViewController.
- Opt-in to App-Bound Domains. The additional
WKWebViewrestrictions from App-Bound Domains ensure that SocialApp is not able to track users using the APIs outlined above.
To opt-in to App-Bound Domains, SocialApp only needs to add an empty
WKAppBoundDomains key to their Info.plist.
<plist version="1.0"> <dict> <key>WKAppBoundDomains</key> </dict>
Since SocialApp does not need any restricted APIs, no
WKWebViewConfiguration arguments are necessary.
Due to the asynchronous nature of the web, a SocialApp developer could see different errors if trying to use restricted APIs and navigate to non app-bound domains in the same
WKWebView. If a
WKWebView in SocialApp uses a restricted API before any navigations occur, and then tries to navigate to a domain outside of the set of “app-bound” domains, the navigation will fail with the error “App-bound domain failure.” Conversely, if SocialApp first navigates to a non-app-bound domain then tries to use a restricted API, the API call will fail.
BrowserApp (exclusively browsing the web)
Another application, BrowserApp, is used exclusively for browsing the web. BrowserApp has previously received permission to take the managed entitlement com.apple.developer.web-browser, which signifies its purpose as a full web-browser. All
WKWebView instances for BrowserApp will therefore have unrestricted API access on all domains. BrowserApp will not need to add a
WKAppBoundDomains value to their Info.plist or make any changes to the way they initialize
HybridApp (both self-hosted + in-app browser)
Finally, let’s look at a more complex example. HybridApp is an application which offers in-app browsing to its users, but also requires restricted API use for
WKWebView instances on its own domain, hybrid.example. HybridApp is a combination of ShopApp and SocialApp, and you should read and fully understand those examples first before considering HybridApp.
HybridApp’s Info.plist might look like this:
<plist version="1.0"> <dict> <key>WKAppBoundDomains</key> <array> <string>hybrid.example</string> </array> </dict>
WKWebViewConfiguration with the specific argument:
webViewConfiguration.limitsNavigationsToAppBoundDomains = YES;.
Say HybridApp tried to use this WKWebView to navigate to shop.example. Since shop.example is not an app-bound domain, this will result in a failed navigation.
Instead, HybridApp can create a different
WKWebView with no
limitsNavigationsToAppBoundDomains configuration flag. HybridApp can use this new
WKWebView to navigate to any domain, including app-bound domains. However, any attempts to call restricted APIs will fail.
The App-Bound Domains feature was created to allow for in-app browsing without sacrificing user privacy.
Prior to iOS 14, the only way to protect web content inside applications was to use SFSafariViewController for general web content, or ASWebAuthenticationSession for authentication purposes. We still think SafariViewController and ASWebAuthenticationSession represent the best way to protect user data, because they are views hosted outside of the application, making it impossible for applications to view or interact with the content of those views. If the developer only wishes to display web content as a convenience to the user, or if they only wish to support a web-based authentication flow, SafariViewController and ASWebAuthenticationSession continue to be the best choices.
Intelligent Tracking Prevention in WKWebView
Additionally in iOS 14.0 and macOS Big Sur, Intelligent Tracking Prevention (ITP), is enabled by default in all
WKWebView applications. To learn more about how ITP protects users against web tracking, checkout this documentation on the topic.
In some extreme cases, users might need to disable ITP protections, for example when relying on web content outside of the app developer’s control. Applications can signal the need to allow users to disable ITP by adding a Purpose String for the key
NSCrossWebsiteTrackingUsageDescription to the app’s Info.plist. When present, this key causes the application’s Settings screen to display a user control to disable ITP. The setting cannot be read or changed through API calls.
Note that applications taking the new Default Web Browser entitlement always have a user control in Settings to disable ITP, and don’t need to specify the
NSCrossWebsiteTrackingUsageDescription key in their Info.plist.
Feedback and Bug Reports
If you find that this feature in any way doesn’t work as explained, please file a WebKit bug at https://bugs.webkit.org and CC Brent Fulgham and Kate Cheney. For feedback, please contact our web evangelist Jon Davis.