Ensure safe code in your iOS app extensions

Ensure safe code in your iOS app extensions

Β·

2 min read

In this blog post, I'll explain the "Require Only App-Extension-Safe API" (APPLICATION_EXTENSION_API_ONLY) build setting in Swift, when and why to use it, and its consequences when generating API documentation.

Whether you implement a widget, share, action, or custom keyboard extension, your app extension runs in a separate process and has limitations on accessing certain resources or performing certain operations. That's why some APIs Are unavailable to app extensions, and an app extension cannot:

  • Access a sharedApplication object, and so cannot use any of the methods on that object

  • Use any API marked in header files with the NS_EXTENSION_UNAVAILABLE macro, or similar unavailability macro, e.g. @available(iOSApplicationExtension, unavailable)

  • Access the camera or microphone on an iOS device

  • Perform long-running background tasks

  • Receive data using AirDrop

Xcode automatically sets APPLICATION_EXTENSION_API_ONLY to YES in the extension target build settings.

When enabled, this causes the compiler and linker to disallow use of APIs that are not available to app extensions and to disallow linking to frameworks that have not been built with this setting enabled.

In other words, the Swift compiler will protect you and throw an error if you attempt to use an unsupported API.

Changing this build setting to NO in your app extension target will cause the error:

Application extensions and any libraries they link to must be built with the APPLICATION_EXTENSION_API_ONLY build setting set to YES.

Let's say you want to share common business logic between your app and your extension, and you start moving code into a framework used by your app and extension. Without further information, the Swift compiler cannot protect you because your framework can be used in an app or extension.

So you also must set the APPLICATION_EXTENSION_API_ONLY to YES in your framework target build settings.

You might need to set certain types in your existing code as unavailable for extensions if those types use unsupported APIs.

@available(iOS 15.0, *)
@available(iOSApplicationExtension, unavailable)
/// API to be used in app but not in extension ...
public class MyApi {
    public init() {
        // ... because UIApplication.shared cannot be used in extensions
        print(UIApplication.shared.canOpenURL(URL(string: "https://www.google.com")!))
    }
}

There is an interesting caveat: those APIs will NOT be documented when using Jazzy, Swift-DocC, or even if you check the Quick Help in Xcode.

The Swift compiler will remove symbols with unavailable for extension annotation from the symbol graph.

Often, you might want to include those symbols in the framework's documentation because the framework can be used in an app target.

The only solution I know is to change that build setting before generating the documentation, e.g. jazzy --build-tool-arguments APPLICATION_EXTENSION_API_ONLY=NO

Did you find this article valuable?

Support Marco Eidinger by becoming a sponsor. Any amount is appreciated!