Skip to content

Using SPI on Apple platforms

Elliott Williams edited this page Feb 26, 2026 · 4 revisions

On some Apple ports (e.g. iOS), WebKit tracks its usage of SPI (private API) via a build-time tool called audit-spi. We use SPI allowlists (1, 2) to document SPI that are in use and ensure there is a bug tracking their cleanup.

During the build, audit-spi analyzes executables to look for use of symbols or Objective-C selectors that are not known public API. When it detects potential SPI, it emits a diagnostic and a suggested allowlist change. On internal builds, audit-spi provides Apple engineers with additional information about how to document new SPI use in internal systems. To continue, update an allowlist and rebuild.

Allowlists are project-specific and stored in each project's Configurations directory. WebKitAdditions has its own project-specific allowlists as well.

Allowlist format

An allowlist is a TOML document that groups SPI declarations by an exception kind explaining why the SPI is being used and bugs tracking the eventual removal of the SPI. Each entry has three parts:

[[<kind>]]
# Bug URLS:
request = "rdar://xxxxxxxxxx"  # or https://bugs.webkit.org/...
cleanup = "rdar://yyyyyyyyyy"

# Allowed names:
symbols = [ "..." ]
classes = [ "..." ]
selectors = [ "..." ]

# Requirements:
requires = [ "..." ]
requires-sdk = [ "..." ]
requires-os = [ "..." ]

The <kind> is one of the categories of SPI exceptions:

  • temporary-usage: Used for temporary workarounds or to adopt SPI before a final API form is available.
  • staging: Used for temporary adoption of upcoming API.
  • not-web-essential: Functionality that another browser vendor would either not use or provide their own implementation.
  • equivalent-api: SPI that has the same behavior as API except in internal builds or testing workflows.

Allowed names

symbols and classes lists denote string names of symbols or ObjC classes respectively.

selectors denote ObjC selectors. Each entry is the list contains a selector name and receiver class. For example:

{ name = "beginExtensionRequestWithInputItems:completion:", class = "NSExtension" }

A class name can be "?" to denote an unknown receiver. Because audit-spi detects ObjC method usage by analyzing a binary's objc_selector table, it cannot check whether an allowed selector is being sent to the expected class. However, the class field is used to disambiguate between multiple methods in the SDK with the same name.

Bug URLs

Each allowlist entry is associated with up to two bugs. The meaning of the two bugs depends on the kind of exception:

Bug type on temporary-usage on staging on not-web-essential or equivalent-api
request Tracks a request to make an API equivalent of the SPI being used. N/A Tracks a request on WebKit to approve this SPI for permanent use, including a justification for why it fits into its exception category.
cleanup Tracks WebKit's adoption of the requested API, once it is available. N/A, though once the API is available in SDKs, audit-spi will require the allowlist entry be removed. N/A

In some circumstances, temporary exceptions don't have a meaningful request associated with them (for example if SPI is being temporarily adopted to work around an unrelated issue), so the request bug is optional.

Requirements

Allowlist entries can be limited to certain build configurations. This makes it possible to gracefully clean up allowlist entries once they are no longer needed. There are three kinds of requirements:

  • requires: A list of <wtf/Platform.h> conditions that must all be active for the allowed SPI to be considered. A condition can be inverted by prefixing it with !.

    # Allowlist entry will only be considered when the build has ENABLE_FOO and not ENABLE_BAR.
    requires = [ "ENABLE_FOO", "!ENABLE_BAR" ]
  • requires-sdk: A list of predicates matching the active SDK version. The syntax of each predicate is "<platform> <comparator> <version number>", for example "iOS < 26.0".

    # Allowlist entry will only be considered when building against a SDK before iOS 26.0.
    requires-sdk = [ "iOS < 26.0" ]

    SDK requirements are useful for tracking when new API are available in system frameworks. Supported comparators are {<, <=, ==, !=, >=, >} though < and <= are usually the most useful.

  • requires-os: A list of predicates matching the OS version being deployed to. The syntax is the same as requires-sdk.

    # Allowlist entry will only be considered when build is targeting an iOS version prior to 26.0.
    requires-os = [ "iOS < 26.0" ]

    OS requirements are useful for tracking SPI emitted into the build from system frameworks (due to inline functions, transparent Swift functions, etc.).

Exhaustiveness

All allowed names must be matched when checking SPI. In other words, it is an error for a declaration to be allowed but not used by any of the input files given to audit-spi. This helps keep the list of temporary exceptions accurate, and it's also a line of defense against accidentally leaking internal-only SPI into public allowlists.

Use requires directives to hide allowed SPI from build configurations where they are not used.

Clone this wiki locally