Implement a private API in Swift

Is there a possibility to implement a private API? In Objective-C there is the private module map. So you can (more or less) "hide" classes and methods from the client.

You can read the Access Control in the Swift Programming Language Guide.

1 Like

There's an experimental annotation you can apply to public methods, @_spi.

public struct MyStruct {
  public init() {}

  @_spi(Private) public func myPrivateFunc() {}
}

Clients that just import ThisFramework will have access to MyStruct and MyStruct.init, but if they want to call myPrivateFunc, they need to annotate their import with a corresponding @_spi annotation with the same Private "SPI tag"

@_spi(Private) import ThisFramework

let s = MyStruct()
s.myPrivateFunc()

You can use whatever SPI tag you'd like, and you can use as many as you'd like within the module.

9 Likes

Wow, this is exactly what I was looking for. Thank you very much @harlanhaskins! I will try this out!

I have two more questions. Does this also work with classes? And can I access the annotated method from another framework?

Because I have implemented several frameworks that partially access each other. Some methods and classes should only be used internally and my customers should not see/use them.

1 Like

Yep, it works with any public declarations.

Are these binary frameworks? If so, these declarations are only added to a .private.swiftinterface file and the compiler will prefer to import that file if it's present, rather than the .swiftinterface file. It may require an extra preprocessing step to remove this file from your built framework directory. There's an Xcode build setting to control whether this file is emitted, SWIFT_EMIT_PRIVATE_MODULE_INTERFACE, but I am not sure how to conditionally set that build setting only when archiving.

2 Likes

My own app imports the normal frameworks. However, I want to provide it to my customers as a binary Swift Package. So I'll have to look into that with the .private.swiftinterface. Thank you very much for your very helpful answers.

I now had the time to test the @_spi annotation and it worked great with your instructions.

Though I noticed that I can still access the annotated method in Objective-C without any problems. @harlanhaskins do you know more about this? Is the annotation to be supported in Objective-C in the future or is it just a Swift thing?

1 Like

I am not sure what the future plans are for ObjC, but it's a common request. @xymus, is this the kind of thing where we could like, guard these declarations with #ifdef SWIFT_<tag>_SPI in the -Swift.h header?

1 Like

This would be very useful for the project that I am working on. Are there any plans to bring the @_spi attribute out of the experimental phase and available as a supported annotation?

Hi, I don't see it in the Xcode build settings. Is this also a private setting, or no longer valid?

1 Like