Skip to content

Build1 publisher3 min readPublished

WKWebsiteDataStore now decides both WKWebView's cookie isolation and its process count

Apple deprecated WKProcessPool in macOS 12 and it no longer affects process sharing, so the data store object you hand a web view is what now determines whether two tabs share WebKit's network and content processes.

The Engineer · Build desk

Illustration accompanying WKWebsiteDataStore now decides both WKWebView's cookie isolation and its process count

What happened

  • Tutorials written before about 2021 tell developers to share a WKProcessPool across web views, but Apple deprecated the class in macOS 12 and it no longer influences process sharing at all.
  • The data store is what decides process sharing now: web views sharing a WKWebsiteDataStore share WebKit's network and content processes, and web views with different stores each get their own.
  • macOS 14 added persistent, named data stores created with WKWebsiteDataStore(forIdentifier: uuid), which the author of the dev.to post calls the API to use for profiles.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • decision Picking a cookie boundary now sets the process bill in the same line of code: a workspace with its own store gets real separation and another set of WebKit processes, and one that shares a store is cheap.
  • cost An app that constructs a store object wherever it needs one passes every functional test and reads the same cookies from disk. In production it pays for duplicate network and content processes.
  • constraint Profile deletion has to be designed around ownership: the app must be able to drop its last reference to a store before the delete call can succeed. That rules out scattering store objects across long-lived views.
  • exposure Sign-in is where the configuration mistake reaches users, since an OAuth popup that cannot see window.opener fails without surfacing an error to the app.

The duplicate-store trap is the expensive one. Call `WKWebsiteDataStore(forIdentifier:)` twice with the same UUID and you get two distinct objects pointing at the same data on disk, and according to the dev.to post nothing crashes and nothing warns [6]. The cookies are shared, but as far as WebKit is concerned those web views are in different stores, so the processes are separate [6]. The fix in the post is a dictionary keyed by profile identity. It returns `.default()` for the standard profile, a named store for an isolated one, `.nonPersistent()` for ephemeral, and caches whichever object it built [7].

A browser with three profiles and four open tabs each, constructing a store object at the point of use, holds twelve distinct stores where the cache would hold three. Every distinct store is its own set of network and content processes [2]. The API surface looks the same in both cases. The disk layout is identical either way [6].

Deletion is where the ownership question becomes concrete. `WKWebsiteDataStore.remove(forIdentifier:)` throws while any reference to the store is alive, and that includes the cached object and a web view you pulled out of the view hierarchy but are still holding [8]. The author writes that he measured retrying on its own, and the retries fail for as long as the reference exists [9]. The sequence that works is to drop the cached object first, then retry up to five times with 250 millisecond sleeps to cover the gap between release and deallocation [10]. That is four sleeps, so about a second of waiting in the worst case [1]. On the fifth failure the snippet returns without throwing, so the caller cannot tell it from a success [3].

The default store cannot be removed at all; you empty it with `removeData(ofTypes:modifiedSince:)` instead [11]. Two code paths for one button labelled "delete everything", and an app with more than one kind of profile needs both [11].

The window.open delegate carries a different constraint. The new web view has to be built with the `WKWebViewConfiguration` WebKit handed you, or the opener relationship breaks. `window.opener` comes back null, named targets stop finding the window, and OAuth popups that post back to their opener fail silently [12]. So the creation helper takes a configuration parameter instead of making its own. Properties set on the `WKWebView` itself are fine there, but user scripts, message handlers and content rules attach at configuration time, which means they have to be attached inside that delegate [13].

For any of this to transfer, it has to hold in the WebKit you ship against. The post is one developer's account of a few months building a tabbed browser with per-workspace cookie jars, downloads and extensions [1], all of it on macOS, with the author saying most of it applies to iOS too [14]. He describes Apple's class references as accurate about the API surface and silent on what happens when several parts are used together [15].

What to watch

  • Whether Apple documents the store-to-process mapping in a class reference, turning observed behaviour into something safe to depend on across releases.
  • Whether a future macOS makes WKWebsiteDataStore(forIdentifier:) hand back the existing instance for a repeated UUID, retiring the app-side cache.
  • Whether remove(forIdentifier:) gains a distinct error for a store that is still referenced, so callers can stop timing deletions with sleeps.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories