Cross-Import Overlay status?

What's going on with cross-import overlays? (original pitch, follow up) As far as I can tell, I can't find any movement on them in the past three years, but they exist in the Apple device SDKs (macOS Sonoma has 20 of them in /System/Library/Frameworks and 6 more in PrivateFrameworks).

I would love to have the ability to make these myself, especially via SPM. Just some casual examples off the top of my head:

  • I have my Time library, and I think it'd be great to add some extensions to SwiftUI to access clocks and regions via the Environment, or modifiers to observe the passing of temporal boundaries, etc. But I don't want to force my users to import SwiftUI by linking it myself, nor do I really want to have a separate TimeUI target. Having an overlay would make these extensions automatically available to users if they've imported Time and SwiftUI.
  • I've wanted to build a nice package that abstracts dealing with system permission prompts, but I don't want to have to import the entire world (CoreLocation, EventKit, ScreenRecording, HealthKit, CoreBluetooth, Photos, Contacts, HomeKit, ...) just to provide shims for things a package consumer might want to use. Having an overlay would make the right things show up to users if they happen to import both.
  • I've got extensions to other frameworks in my Repo Of Swift Extensions that I don't always need; it'd be nice to shift those out into overlays so I only get the binary increase size at link time if I actually need them.

So … where are things at with these?

34 Likes

So I guess the answer here is that they've been abandoned and we won't get to use them? They'll remain an Apple-only tooling feature? :cry:

4 Likes

I know it’s not quite the same, but with the acceptance of SE-0450, perhaps now you can define a trait in SPM for each overlay respectively.

3 Likes

That is an extremely intriguing idea. It does put a bit of burden on the client to make sure they define the correct traits, but I can think of ways to mitigate that as well.

Thank you for the idea! I need to go experiment :grin:

1 Like

Very interesting indeed!
I’ll keep an eye on this thread; I’m very interested in this subject (System vs. SystemPackage in my case).

1 Like

FYI package traits are now available in 6.1 development snapshots (proposal metadata hasn't been updated yet): Mark SE-0450 (package traits) as implemented in 6.1 by MaxDesiatov · Pull Request #2641 · swiftlang/swift-evolution · GitHub

2 Likes

This is definitely one use-case we had in mind for package traits. While they don't automagically work when a module depends on both module A and B to expose these cross-overlay imports, they do allow a package to expose a feature that enables an optional dependency and additional API depending on that optional dependency.

I am really looking forward to see how we adopt those in the ecosystem.

1 Like

I downloaded Xcode 16.3 beta 1 the other day to try out this approach, and I'm pleased to report that it mostly works.

The Good

  • I can create a package with traits, and the trait names get exposed in code as active compilation conditions.

  • I can depend on this package in another package and enable the traits in the .dependency(...) statement

  • With these together and some creatively-applied @available(*, unavailable, message: "...") statements, I can make a package that proactively tells clients that they're trying to use an API without the proper trait enabled.

The Meh

  • Conditional compilation is kind of gross. It balloons the complexity of the code when used within method bodies, and using it to conditionalize the existence of methods themselves means that there's a constant battle to make sure that the true-branch method matches the false-branch method. I will need to investigate better ways to organize the code to see if I can mitigate this. Perhaps multiple targets combined with some @_exported imports? Or complex source file inclusion based on trait enablement somehow?

  • It's a stringly-typed API, which means it's easy to fat-finger a trait name. Yes, the compiler will tell you that the mis-typed trait, but there is zero code-completion.

  • I'm surprised that the trait applies to the entire package and not to a specific dependency. It's common to deal with packages that have multiple products, and I'm surprised that each product has to use the same set of traits. Or you could easily imagine a package that defines a "debug" trait to enable extra introspection capabilities. A client of the package would only want to use that for specific targets, not every target. The fact that you cannot specify traits on a per-usage basis seems like a pretty unfortunate design.

The Ugly

  • Xcode has no support for any of this in Xcode projects. There is no way to specify active traits when depending on a package. This is a feature that is only available to other packages. This means that, in its current form, it essentially unusable to me without the tedious chore of creating intermediate packages.

  • Additionally when editing, there is no way to discover what the compilation condition of the trait actually is. I have to know a priori how the compiler will translate my trait name into a condition. Since trait names are strings that can contain things like spaces or emoji or slashes or zalgo text... Once again I'm left completely high-and-dry by the tools to use this in any sort of way that is a single step beyond the most rudimentary:

edit: aha, it turns out that particular screenshotted trait name isn't legal. However, Xcode will still succeed building the package even though the package itself is invalid. :rofl: The point still stands though about the un-discoverability of condition names:


This feels like a step in the right direction, and I appreciate the hint to try this @tevelee! I can get this to mostly work right now, but I'm left wanting a whole lot more from the tooling.

12 Likes

Thanks for the great feedback @davedelong and trying out the new feature! Really appreciate you putting in the time to explore it and write up your experience here.
I think all of those are valid points and some of them I expected to pop up. Our goal was to land a solid foundation that we can expand on and improve the developer experience over time.

Could you help us and file issues for the missing features and for Xcode file a feedback request asking for better integration?

1 Like

It would've been the work of a couple minutes to add it based on my feedback here, but here we are two days later:

SPM: Traits apply to packages instead of products
SourceKit-LSP: No way to discover a trait's compilation condition
#FB16837991: Xcode is missing all SPM Traits integration

8 Likes

I've been trying to find support for this in the Xcode 26 betas but as of beta 4 I still can't find anything. Is support still pending? Can we hope to see this resolved before release? This is a really great feature and it's a shame that packages can't really use traits right now without the major caveat that package users can't manage such a dependency in Xcode.

5 Likes

I didn’t find anything either.