I work in a corpulent iOS monorepo with 100s of Xcode projects in an Xcode workspace, and maintaining these has been a tedious task (custom scripts to update dependencies, framework references, sort project files, etc). Therefore, like many, we thought SPM with its abstracted and simplified module management would be a viable solution to modularization in our sprawling codebase!
After a cursory spike, we started migrating our 'core' frameworks at the root of the dependency tree into a single SPM package located at the root of the repository. Essentially, all of our shared frameworks. Fast-forward to today and it's been arguably one of the most painful and frustrating experiences in my career. Surprisingly, upgrading to Xcode14.1 has only made it worse.
I won't get into the myriad of issues, but here are them at a high level:
SPM is categorically untenable for external (remote) dependency management in medium-sized and larger projects
The number of 'resolve' issues related to SPM failing to download remote dependencies due to SSH, Git, cache, random resolve errors, etc. issues is innumerable. The performance, hidden and bizarre errors, poor caching, and developer support led to us ditching remote dependencies completely.
Instead, we went back to using Carthage to download our external dependencies. We integrate them into SPM using local binaries; this provided stability, an incredible performance boost, and developer productivity increase; but also disenchantment.
Poor configuration options
With an Xcode project, you can configure your targets to be built differently depending on the environment via xcconfig
files. We use these extensively to toggle settings such as "treat warnings as errors", "warn about type long type checking", etc. in different environments such as CI.
SPM does not provide an acceptable way to do this—especially if you are using SPM in a monorepo for iOS. We use test plans to build and test our packages which means we don't build our packages via swift package
. We ended up reading a JSON file that only existed in CI from the Package manifest to enable "treat warnings as errors" to preclude an influx of warnings slinking in; a complete hack for something that Xcode has supported for decades.
Slow builds: Create Build Description
The 'Create Build Description' in Xcode has seemingly gotten slower now that we've started using SPM (I cannot however verify SPM is to blame).
Primary issue: 'Resolve packages...' destroys developer productivity... even when they're all local!
Previously in Xcode 13, we were able to resolve the package(s) in our workspace from the command line to prevent Xcode from spinning upon opening. With Xcode 14.1, resolving packages from the command line does nothing, and Xcode completely hangs and beach balls for ~5-20s to resolve LOCAL packages when opening Xcode. We don't have ANY remote dependencies and it can take upwards of 20 seconds to resolve our manifest, which as of today has 10 products and ~20 targets, and depends on one local package that contains 2 products and 2 targets.
Xcode has even started to hang (~10s) when being closed (could be unrelated).
However, that's not even the worst part, performing simple and common operations such as adding a file to a package target cause Xcode to start resolving again! When the 'Resolve Packages' occurs, Xcode again hangs (beach balls) for 2-3s. Note, adding a file to one of the Xcode projects in the workspace is instant.
This is all using M1 Pros and M1 Pro Maxes with a minimum of 32 GB of RAM.
One thing to note is that opening the Package outside of the Xcode workspace is faster, but 'Resolves' still occur for the same operations (e.g. adding a file).
Questions:
-
How does one prevent Xcode from spuriously performing 'Resolve Packages' when performing basic operations like adding a file or switching branches (mind you, 0 changes to the
Package.swift
file)? -
Is there any way to disable
Resolve Packages
entirely and rely on manually running it? Does Xcode really need toResolve packages
every time a file is added (even when the file is outside of any of the package targets paths)? -
Does anyone have troubleshooting tips and ideas to understand why Xcode needs to
Resolvle Packages
when the manifest never changes? Spending minutes a day on an M1 Pro opening Xcode and adding files is rather frustrating. -
Why does
Resolve Packages...
cause Xcode to hang at all? Shouldn't this be a background operation? -
Will the performance of SPM improve (esp. with Xcode)? Should we abandon the ship and return to our custom Xcode generation scripts?
At this point, we've halted the migration of non-shared frameworks to SPM due to the increasingly high cost of stymying developer productivity.
Thanks for following along and I appreciate any advice, recommendations, or moral support.