Use of SwiftPM for internal modules?

Actually... it can indeed happen when you have a remote package dependency but it specifies a local dependency path. You might wonder why that would ever happen, but I've run into this several times.

The best workaround that I've found is to use an environment variable to switch from the local to the remote at build time as needed:

Then below:

(using the swiftXState global variable in the dependencies: array)

Note: if you're using Xcode, then you have to take non-obvious steps to get ProcessInfo in your Package.swift to actually see any env vars. Because it certainly doesn't read them from your Scheme or .xcproj build settings env vars. No no... and it doesn't even read them from your shell .*rc or .profile, etc.

The only solution that I've found is to jerry-rig launchctl:

launchctl setenv MY_VAR "my value"

... then quit and reopen Xcode.

If you need it to be persistent for your user login on your Mac, you can make a .plist like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
 "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>com.jon.env</string>

    <key>ProgramArguments</key>
    <array>
      <string>/bin/launchctl</string>
      <string>setenv</string>
      <string>MY_VAR</string>
      <string>my value</string>
    </array>

    <key>RunAtLoad</key>
    <true/>
  </dict>
</plist>

... and save that into ~/Library/LaunchAgents/com.makeXcodePlayNiceWithSPM.env.plist

Then of course the first time you add this, do the following raindance to avoid having to reboot/relog your Mac:

launchctl bootstrap gui/$UID ~/Library/LaunchAgents/com.makeXcodePlayNiceWithSPM.env.plist
launchctl kickstart -k gui/$UID/com.makeXcodePlayNiceWithSPM.env.plist

... then quit and reopen Xcode, and tattoo onto your left arm what you have done so that later you can actually remember how to disable that env var when needed.

(If anyone knows a better way to make Xcode's SPM see env vars via ProcessInfo, certainly let me know. There has to be a better way... right?) :sweat_smile:

Normally I'm annoyed when old threads are bumped, but nothing has really improved in Xcode's SPM integration since 2021, so most of this discussion still applies. Xcode still messes up when choosing between static and dynamic integration and you still have no control over what it chooses. Since 2021 this issue has spread to normal framework targets as well, where Xcode can't reliably decide how to link packages into them, and then decide how to link the framework into the app target. Lately Xcode has been forcefully choosing to build our packages for Intel, despite setting all the settings we can find to only the active architecture, and setting x86_64 as an ignored arch. Xcode's SPM DX is still worse than VSCode's, and you still can't override a tree dependency with your own fork (though that's at least a fundamental SPM limitation and not just an Xcode bug). Xcode 27 revealed a whole new AI workflow system and multiple MCP integrations, as well as the new Device Hub, but the SPM DX has barely changed since the initial integration.

What's the workaround for this? Make a local copy of the whole tree and edit all the Package.swift files to point at the local repos? (Sounds like a huge pain... might be easier to do fun things with a local DNS remapper to make SPM think it's talking to a certain remote repo when really it's talking to your local fork. But I haven't run into this issue myself yet, so I'm wondering what you did.)

Any idea why SPM has this limitation?

How does this manifest exactly? The last time I had to deal with something like this was back in '22, I seem to recall we ended up having to build XCFrameworks around dependencies and wrap those in Swift packages to make XCode happy. Lately I've just been working with static libs and everything's hunky dory in XCode 26/27 beta.

Seems odd, you'd think it would be featured rather centrally. Why do you think they would not focus on that? If you give me steps to repro some of your issues I can try to write up some reports for them.

Builds that randomly succeed for fail depending on whether Xcode chooses to build a particular dependency statically or dynamically. This was especially exacerbated after we added a watchOS target, which caused Xcode to start building all of our packages for Intel for no apparent reason. We couldn't get our shared code framework to build its dependencies reliably when sharing between the iOS and watchOS targets because Xcode couldn't decide how to build them. Eventually we just rolled back the shared framework and now just have a directory of files that are included in both targets. Xcode still builds all of our packages for Intel but at least it does so reliably.

1 Like

It's all coming back to me. My traumatic memory had blocked out my past experiences with SPM/Xcode.

Apple recommends SPM packages do not specify static or dynamic so that "so Swift Package Manager can choose between static or dynamic linking based on the preference of the package’s consumer." (library(name:type:targets:) | Apple Developer Documentation)

But it seems if dependency A a links C dynamically but B links C statically, your Xcode linking can become nondeterministic.

I recall having to do BS like this:

I wonder how many of the Xcode/SPM issues I reported back in 2021 are fixed now: [Pitch] Swift Projects - #3 by 1oo7

I seem to recall we ended up doing something crazy like creating a separate .xcproj that would build an XCFramework containing ALL of our Swift Packages, or maybe it was a single Swift Package that linked to all of our other Swift packages... it's been 4+ years, hard to remember.

Though I do wonder how much of this is SPM and not Xcode. SPM feels more like something from the "uncompiled languages" world, like node.js/python where you can "npm add" whatever you want without a care in the world and it just works because there's no concept of "building" or "packaging" or "code-signing" or "platform architecture" to worry about. Trying to make this kind of model work for a language like Swift seems inherently optimistic.

It starts to make sense why so many packages advertise themselves as having "no dependencies!"

That said, I'm not here to trash SPM. I really actually am having a great experience these days working on small projects with Xcode and SPM. Without any interdependencies between packages, and with most of them being my own creations, it becomes actually pretty nice.