Building Profiles with new WebKit API

Profiles are useful. They allow users to organize their data into categories and manage them separately. Users can create different profiles for different purposes and delete any profile without worrying about messing with data from another profile. Profiles also make it easier to access different online identities. Users with multiple accounts on the same site can simultaneously log into them within different profiles. They don’t need to keep logging in and out to switch between accounts because cookies are separate between profiles.

An essential requirement to implement profiles is to have separate containers for website data. In WebKit, this means the client needs to have different data stores. Before macOS 14 and iOS 17, WebKit clients could have multiple non-persistent data stores, but only one persistent data store — the default one. Non-persistent data stores do not store data to disk, so they cannot be retained across sessions.

In macOS 14 and iOS 17, we’ve introduced a new set of APIs to let clients create and manage multiple persistent data stores. Let’s take a look at how to use them to build a “profiles” feature for your app.

Create a profile

When a user creates a profile, you can create a custom persistent WKWebsiteDataStore. To do that, you should provide an identifier that can uniquely identify the store. You will need to remember the identifier so the data store can be retrieved and the profile can be restored when your app relaunches.

let profileIdentifier = UUID()
let profileDataStore = WKWebsiteDataStore(forIdentifier: profileIdentifier)

Then you can assign the newly created WKWebsiteDataStore to a WKWebView via configuration, and use WKWebView to load web content using the new profile, see https://developer.apple.com/documentation/webkit/wkwebview/ for example.

let configuration = WKWebViewConfiguration()
configuration.websiteDataStore = profileDataStore
let webView = WKWebView(frame: .zero, configuration: configuration)

Delete a profile

When a user deletes a profile, you can delete its data store based on its identifier. The operation can fail if the specified data store is still in use, for example, by an active WKWebView that is displaying web content, or if there is a file system error. You will need to catch and handle the error properly.

do {
    try await WKWebsiteDataStore.remove(forIdentifier: profileIdentifier)
} catch {
    print("Removing profile failed with error: \(error)")
}

To find out if a WKWebView is using a data store, you can get the identifier of a WKWebsiteDataStore. Note that non-persistent WKWebsiteDataStores and the default WKWebsiteDataStore do not have identifiers.

if webView.configuration.websiteDataStore.identifier == profileIdentifier {
    print("Profile is in use")
}

Retrieve an existing profile

With the identifier, you can retrieve an existing data store. However, that would create a data store if one does not exist yet. If you just want to check if a data store with a specified identifier exists, not create it, you can use fetchAllDataStoreIdentifiers to get all the identifiers.

let identifiers = await WKWebsiteDataStore.allDataStoreIdentifiers
if !identifiers.contains(profileIdentifier) {
    print("Cannot find profile")
}

What’s next?

A profile may contain many other types of data belonging to the client, and not to WebKit, such as per-site settings, bookmarks, browsing history, etc. Those types are not covered by this API and you may need to separate them too to make the profile experience more complete.

If you want to add support for profiles in your app, trying out the new WebKit APIs is definitely a good start and an essential step. If you have any feedback or encounter any issue when using the API, please let us know by filing a bug on bugs.webkit.org.