Tech stories

Preventing Accidental API Breaks: A Swift Developer's Guide to API Diffing

by Alex Guretzki, iOS Developer, Adyen

December 13th, 2024
 ·  4 minutes
A desktop that shows reconciliation or administration in the back office, where revenue is checked.

Picture this: Your team just shipped a new SDK version, and suddenly, your Slack channels light up with messages from frustrated developers. The reason? An accidental breaking change in your public API that slipped through code review. Sound familiar?

The Challenge of API Stability

As SDK developers, we struggle to evolve our APIs and maintain stability. One wrong move—like removing a parameter or changing a method signature—can break our clients' applications. While Swift's robust type system helps catch many issues at compile-time, detecting API-breaking changes before they reach production requires specialized tooling.

Available Solutions: From Simple to Sophisticated

Available Solutions: From Simple to Sophisticated

In this article, I will present the available solutions with their benefits and challenges:

  • Pure Swift Package Manager

  • xcodebuild & swift-api-digester

  • Swift Public API diff

Swift Package Manager's Built-in Tool (and Why It's Not Enough)

A first approach to detect breaking API changes might be to look at SPM. It ships with a tool that promises to detect API-breaking changes by comparing them against a previous version:

However, there's a catch: while this works great for pure Swift packages, it falls short for projects targeting iOS. Even with attempts to force iOS SDK usage:

For more details about the drawbacks, refer to discussions on the Swift Forums and this thread.

A More Robust Approach: xcodebuild + swift-api-digester

This combination offers a more suitable option for iOS projects. Here's how to use it:

1) First, build your project

2) Run the command to diagnose API changes

3) Verify the output with the differences

The Power Move: Swift Public API Diff

We created and made open-source the Swift Public API Diff tool as an alternative to swift-api-digester. Swift Public API Diff provides more detailed and contextual information, making it simpler to understand changes during code reviews.

It provides a detailed summary of changes and lets you specify the desired access level. As an open-source project, it can be customized to meet individual needs and easily integrate into your existing workflows.

Swift Public API Diff leverages `.swiftinterface` files that contain rich information about your APIs.

Here's how to generate these files independently.

The .swiftinterface files are located inside the .swiftmodule directory of the scheme within your specified -derivedDataPath.

Depending on the usage of the @_spi and/or package access modifiers, there can be multiple .swiftinterface definitions with the following extensions:

  • .private.swiftinterface (exposes @_spi interfaces + package & public)

  • .package.swiftinterface (exposes package interfaces + public)

  • .swiftinterface (exposes only the public interface)

The Swift Public API Diff can generate the .swiftinterface files for you and uses swift-syntax to parse these files into a Swift model, which allows the elements to be compared individually.

Below, you can find an example of how you run the diff on two remote versions of a GitHub project:

For more examples and use-cases you can refer to the Github page.

The output is formatted in an easy-to-understand way, highlighting additions, removals, and changes.  

Here is example of how the markdown output looks as a Github comment:

Best Practices for Maintaining API Stability

Delivering stable APIs is crucial to ensure that new versions do not affect existing applications while still incorporating new features and bug fixes.

  1. Add API diffing to your CI pipeline: This helps catch breaking changes before they make it to your main branch. Check out how Adyen iOS uses GitHub actions to detect API changes.

  2. Use semantic versioning: Adopt semantic versioning to communicate the nature of changes to your users.

  3. Document breaking changes: Keep a clear changelog and provide a migration guide for major SDK upgrades.

  4. Plan for API evolution: Always consider backward compatibility when designing new features.

  5. Deprecation strategy: Announce deprecations in advance to ensure developers are prepared when functionalities are removed from the SDK.

Taking Action

Start by integrating API diffing into your development workflow:

  1. Choose the tool that best fits your needs (At Adyen, we've made Swift Public API Diff an indispensable tool for our iOS projects)

  2. Set up automated checks in your CI pipeline.

  3. Establish a process for reviewing and communicating API changes.

  4. Create guidelines for API evolution in your team.

Remember: The best API break is the one that never happens. With these tools and practices in place, you can evolve your SDK with confidence while keeping your users happy.

Ready to try it out? Start with Swift Public API Diff in your next PR review and see the difference it makes in catching potential breaks before they reach your users.

Fresh insights, straight to your inbox

By submitting your information you confirm that you have read Adyen's Privacy Policy and agree to the use of your data in all Adyen communications.