When reading Swift Testing code, I found the @_spi attribute. I wasn’t familiar with this attribute, so I looked it up in the Apple Developer Documentation but couldn’t find it. Therefore, I researched the @_spi attribute and summarized the results in this article.

What is the _spi attribute?

We can find the detailed documentation on the @_spi attribute in Swift Syntax.

Functions marked as @_spi(RawSyntax) (where RawSyntax can be any name) are considered SPI (System Programming Interface) and are only accessible if the module that declares them is imported as @_spi(RawSyntax).― SPI.md ―

Swift 5.3 brought us the @_spi attribute by this pull request: SPI support in Swift #29810.

Add experimental support to define System Programming Interfaces (SPI) in Swift. An SPI is a kind of API targeted at specific clients and that is hidden by default. In practice, a Swift library developer could mark declarations as SPI if they are reserved for a closely-related library or client, or if they are experimental and may be modified without warning in the next version of the library.

Definition and importation of SPIs is declared with the @_spi attribute. A public declaration marked with @_spi(SPIName) is usable only from within the same module and by clients that import the module with the compatible @_spi(SPIName) attribute.― PR #29810 description ―

The _spi attribute in Swift Testing

We can find the definition of SPI groups in Swift Testing in the document.

  • @_spi(ForToolsIntegrationOnly) for interfaces used to integrate with external tools.
  • @_spi(Experimental) for interfaces that are experimental or under active development.

In the confirmation article, I wrote last time, @_spi(Experimental) was used.

@_spi(Experimental)
public func confirmation<R>(
  _ comment: Comment? = nil,
  expectedCount: some RangeExpression<Int> & Sendable,
  isolation: isolated (any Actor)? = #isolation,
  sourceLocation: SourceLocation = #_sourceLocation,
  _ body: (Confirmation) async throws -> sending R
) async rethrows -> R {
  let confirmation = Confirmation()
  defer {
    let actualCount = confirmation.count.rawValue
    if !expectedCount.contains(actualCount) {
      let issue = Issue(
        kind: expectedCount.issueKind(forActualCount: actualCount),
        comments: Array(comment),
        sourceContext: .init(backtrace: .current(), sourceLocation: sourceLocation)
      )
      issue.record()
    }
  }
  return try await body(confirmation)
}

Conclusion

Swift 5.3 introduced the @_spi attribute to define System Programming Interfaces (SPI). In Swift Testing, @_spi(ForToolsIntegrationOnly) and @_spi(Experimental) are used. If you are operating a Swift library, the @_spi attribute can help you provide experimental Private APIs to developers.

Thank you!