Wednesday, September 30, 2026

Backporting SwiftUI APIs

Dave DeLong:

I can improve the process of removing backwards compatibility shims by using conditional compilation to remind me when they’re no longer necessary!

[…]

This does mean that the first time we change our deployment target, we’ll get a bunch of compilation errors. But given the nature of how Backport is implemented, this should be a relatively quick process to move past. (And of course, you’re welcome to use #warning instead of #error).

Shaps Benkau:

SwiftUIBackports mirrors Apple’s SwiftUI API names, shapes, behavior, documentation, and availability where practical. This keeps app code familiar today, and easy to migrate away from once your deployment target reaches the native API.

Backports are discovered through .backport, the Backport namespace, or backport-prefixed environment values.

Majid Jabrayilov:

Usually, we don’t see many new APIs in intermediate releases like 27.1, but this time is different. Apple introduced several new iPhone Duo-related APIs in the 27.1 release. This week, we will learn how to use all of these new APIs without bumping your project’s target version.

[…]

Here we introduced the toolbarVerticalBehavior function on the Backport type that handles availability checks and calls the new APIs if available or returns a plain view when it is not available.

Previously:

2 Comments RSS · Twitter · Mastodon


I don't get the advantage of using the Backport namespace. Is there any downside to shadowing the actual modifier with @_disfavoredOverload and calling back to the official API where supported?

Here's an example. If you wanted to, you could also emit a warning, if no longer necessary with the method from Dave. Once no longer necessary, I only need to remove the function and don't have to touch any call-site.

extension View {
    @_disfavoredOverload @ViewBuilder
    func safeAreaPadding(_ edges: Edge.Set = .all, _ length: CGFloat? = nil) -> some View {
        if #available(iOS 17, *) {
            safeAreaPadding(edges, length)
        } else {
            safeAreaPaddingBackport(edges, length)
        }
    }
}

@Melvin I’m not sure, but does your way only work if the extension is defined in the same target as where it’s called?

Leave a Comment