Home › Mobile › Xcode Module Not Found After SPM Add: Quick Fix
Beginner 5 min · September 23, 2026

Xcode Module Not Found After SPM Add: Quick Fix

Link the package product to your target, import the product name, and clean derived data.

N
Naren Founder & Principal Engineer

20+ years shipping production backend systems. Notes here come from systems that actually shipped.

Follow
✓ Production
production tested
September 27, 2026
last updated
2,085
articles · all by Naren
Before you start⏱ 8 min
  • ✓Xcode with a basic iOS project
  • ✓Adding files to targets
  • ✓Terminal basics for cleanup
 ● Production Incident 🔎 Debug Guide
⚡Quick Answer
  • Xcode resolves packages per project but links them per target, so each target that imports must list the product
  • Import the product name declared in Package.swift, which often differs from the repo name you pasted
  • Resolve package versions fully, then clean derived data and restart Xcode before rebuilding
  • Pin dependencies to versions and commit Package.resolved so every machine and CI runner builds identically
✦ Definition~90s read
What is Xcode Module Not Found After Swift Package Manager Add?

Swift Package Manager structures code in three layers. Targets are the package author's internal build units containing sources. Products are the named libraries the package vends to clients; these are what import statements reference. Dependencies are the versioned requirements your project declares, resolved into checkouts recorded in Package.resolved.

★
Picture a library that stocks a new book series on its shelves.

Confusing the layers is the root of most module errors.

Xcode processes those layers in order. Resolution downloads the declared dependencies into shared checkouts. Linking attaches chosen products to each of your targets individually, placing their modules on that target's search path. Compilation then resolves your import lines against the linked modules.

A failure at any stage reports as a missing module, so the fix depends on which stage actually broke.

Derived data sits beneath all three stages as a cache of checkouts, built modules, and graph indexes. It accelerates rebuilds enormously and corrupts occasionally, especially around interrupted downloads and branch switches. Cleaning removes products; deleting derived data rebuilds the graph. Knowing which level your symptom lives at separates a ten-second fix from an hour of flailing.

Versioning ties the system together across machines. Rules like from: declare acceptable ranges, Package.resolved pins exact checkouts, and branch tracking follows moving code. Committed resolved files plus bounded rules give every developer and CI runner identical sources. That reproducibility is what turns package management from superstition into infrastructure your team can reason about.

Plain-English First

Picture a library that stocks a new book series on its shelves. Resolving is the delivery truck arriving. Linking is putting the books on your branch library's shelf. If your branch never shelved them, asking the librarian finds nothing, even though the books are in the building. Shelving your branch is linking the product to your target, and shelving every branch covers tests and widgets too.

You add a Swift package, watch it resolve with a satisfying checkmark, type import, and Xcode answers no such module. The package sits right there in the navigator, documented and promising, while the compiler insists it doesn't exist. Cleaning doesn't help. Readding doesn't help. Restarting feels like superstition.

The gap is that resolving and linking are separate steps. Resolving downloads the package into the project. Linking attaches its products to the specific target being built. Xcode resolves once per project but links per target, so your import fails in exactly the targets you forgot to connect, including test bundles everyone forgets.

Beginners hit this alongside three lookalike failures: importing the repo name instead of the product name, building on stale derived data mid-download, and tracking a branch that moved under them. All four produce the same gray error with different cures.

This guide shows you how to confirm target membership, import the true product name, clean derived data correctly, restart Xcode at the right moment, and pin versions so the fix sticks. Five minutes of systematic checks beats an hour of re-adding packages.

A Swift package joins your project in two distinct moves. Resolving fetches the code and records versions; it happens once per project and puts the package in the navigator. Linking connects a package product to a build target; it happens once per target and puts the compiled module on that target's search path. The navigator checkmark proves the first move. Only a successful import proves the second.

Open the failing target's General tab and read Frameworks, Libraries, and Embedded Content. If the package product isn't listed, the compiler literally cannot see the module no matter how healthy the checkout is. Test bundles, widget extensions, and watch targets each keep their own list, which is why the app builds while its tests fail with the identical import line.

The command line confirms the same facts without clicking. xcodebuild -list shows schemes and targets so you verify which target actually builds. xcodebuild -resolvePackageDependencies forces resolution to complete and reports checkout errors as text. Use these when CI fails but Xcode looks fine, since headless builds expose link gaps the IDE's cached state hides.

Make membership review part of adding any package. The developer who adds it links every importing target immediately, including tests, and the pull request shows each target building. A package added for one target and imported by two is a demo-day failure filed in advance.

check-links.shBASH
1
2
3
4
5
6
7
# Confirm which targets link the product:
# Target > General > Frameworks, Libraries, and Embedded Content
# Each importing target (app, tests, widget) must list it.

# Command-line check of package resolution state:
xcodebuild -list
xcodebuild -resolvePackageDependencies -scheme ShopApp
📊 Production Insight
A widget target left unlinked for months failed only at the demo archive. Rule: build every target, not just the app, before calling a dependency done.
🎯 Key Takeaway
Resolution downloads once per project; linking attaches per target. Verify every importing target lists the product, tests and widgets included.

Import the Product Name, Not the Repo Name

Packages have three names that routinely differ: the repository name, the internal target names, and the product names clients actually import. The repository is the URL slug you pasted. Targets are the package author's build units. Products are the libraries the package vends, and only product names work in import statements. Guessing among the three fails two times out of three.

Read Package.swift of the dependency and find the products array. The name field of the .library entry is your import string, exact case included. In Xcode you can also select the resolved package in the navigator to see its products without opening the manifest. Copy that string into your import rather than reconstructing it from the URL.

Mixed packages multiply the confusion by vending several products from one repo, like AnalyticsCore alongside AnalyticsUI. Importing the umbrella repo name compiles nowhere; each file imports exactly the product it uses. When a tutorial's import stops working, the package usually renamed a product between major versions while the repo URL stayed put.

Lock the discovery into the codebase. Add a comment or a central wrapper file naming the product and its pinned version, so the next developer doesn't re-derive the string from memory. Grep for the old wrong import after fixing, because copy-pasted files spread the wrong name faster than the fix.

FeedView.swiftSWIFT
1
2
3
4
5
6
7
// Package.swift of the dependency:
// products: [ .library(name: "AnalyticsCore", targets: ["Analytics"]) ]
// repo name: analytics-ios, target name: Analytics

import AnalyticsCore // the product name: this compiles
// import analytics-ios // wrong: the repo name never compiles
// import Analytics // wrong unless a product shares the target name
📊 Production Insight
A renamed product broke imports while the repo URL stayed identical. Rule: copy import names from Package.swift products.
🎯 Key Takeaway
Only product names are importable. Read them from Package.swift products and copy exactly; repo and target names mislead.

Resolve Fully, Then Clean, Then Build

Package resolution is asynchronous: Xcode downloads checkouts, verifies versions, and generates module maps in the background. Building mid-resolution compiles against partial checkouts with missing module maps, which produces no such module errors that no amount of cleaning fixes, because the underlying download still hasn't finished. The navigator's progress badges are the signal most developers ignore.

The correct sequence is resolve, wait, clean, build. Trigger File, Packages, Resolve Package Versions or the xcodebuild equivalent, watch every package badge turn solid, then clean the build folder and compile. Each step depends on the previous one completing: cleaning mid-download just deletes scaffolding the resolver still needs.

Corrupt checkouts need stronger medicine. A branch switch, a killed Xcode, or a disk-full moment can leave SourcePackages half-written in a state resolve alone won't repair. Deleting the SourcePackages checkout folder under derived data and resolving again fetches pristine copies. This preserves your project settings while replacing exactly the damaged layer.

Teach the sequence to the team as law: badges solid before build, resolve before clean, clean before rebuild. Paste it in the repo readme next to the setup steps. Random re-adding of packages is how developers spend an hour fixing a ninety-second download wait.

resolve-clean.shBASH
1
2
3
4
5
6
7
# Force a full resolve, then clean before rebuilding:
xcodebuild -resolvePackageDependencies -scheme ShopApp
xcodebuild -scheme ShopApp -configuration Debug clean build

# If checkouts look corrupt, reset them and resolve again:
rm -rf ~/Library/Developer/Xcode/DerivedData/ShopApp-*/SourcePackages
xcodebuild -resolvePackageDependencies -scheme ShopApp
💡Never Build Mid-Download
Building while the package badge still shows progress compiles against a half-downloaded checkout. Wait for solid badges, then build once and trust the result.
📊 Production Insight
Building mid-download wastes hours that waiting ninety seconds would save. Rule: badges solid before build, enforced as team law.
🎯 Key Takeaway
Wait for resolution badges to clear before building. Resolve, clean, and rebuild in order; reset SourcePackages when checkouts corrupt.

Derived Data, Restarts, and Stale Module Graphs

Derived data caches the module graph: resolved checkouts, built products, indexes, and the maps connecting imports to files. When packages change underneath it, through upgrades, branch moves, or interrupted downloads, the cache describes a project that no longer exists. Clean removes build products but preserves the graph; only deleting derived data rebuilds the map from current truth.

Restarting Xcode matters because the IDE holds the package graph in memory alongside the disk cache. Closing the window leaves the process and its stale graph running. Quitting fully, deleting derived data, and reopening forces both layers to reconstruct from the project file and fresh checkouts. Either step alone fixes half the cases; together they fix nearly all.

Recognize the stale-cache signature: the module worked yesterday, nothing in your diff touches packages, and teammates build fine. Your disk cache diverged from shared truth, usually via an interrupted resolve or an uncommitted Package.resolved. The fix is local hygiene, not project surgery, so resist re-adding the package or editing search paths.

Don't let the reset become a habit that hides real bugs. If you delete derived data weekly for the same package, something structural is wrong: an unpinned branch, an uncommitted resolved file, or a post-install script fighting Xcode. Fix the structure after unblocking yourself, or the stale cache will return on schedule.

full-reset.shBASH
1
2
3
4
5
6
# Full reset when the module graph goes stale:
# 1. Quit Xcode completely (not just close the window).
# 2. Delete derived data:
rm -rf ~/Library/Developer/Xcode/DerivedData/ShopApp-*
# 3. Reopen the project, wait for package badges to clear.
# 4. Product > Clean Build Folder, then build.
📊 Production Insight
Weekly derived-data deletions stopped once the branch dependency got pinned. Rule: treat recurring stale caches as a dependency bug.
🎯 Key Takeaway
Quit Xcode fully and delete derived data to rebuild the module graph from truth. If resets recur, fix the unpinned dependency underneath.

Version Rules and Package.resolved That Reproduce

Version rules decide which code a fresh checkout receives. A from: rule takes the latest compatible release, an exact pins one release, and a branch tracks a moving target. Branches feel current but make builds unreproducible: Monday's checkout and Friday's checkout are different code with the same project file. Releases and demos must never track branches.

Package.resolved records the exact versions last resolved, and committing it makes every machine and CI runner check out identical code. Without it, two developers with the same version rules can resolve different releases on different days, and only one of them sees the new API. The resulting works-on-my-Mac module error wastes exactly the afternoon you needed for the demo.

Upgrades deserve a deliberate flow. Bump the version rule, resolve, read the package's changelog for renamed products and raised deployment targets, fix imports across all targets, and commit the rule plus the resolved file together. Reviewers then see the dependency change as one atomic diff instead of a surprise.

Lint both halves in CI: fail builds when Package.resolved is uncommitted or when any dependency tracks a branch on release schemes. These checks cost minutes to write and save the demo-day scramble permanently. Reproducibility isn't paperwork; it's the difference between debugging your code and debugging your checkout.

Package.swiftSWIFT
1
2
3
4
5
6
7
8
// Package.swift (your app): prefer bounded version rules
dependencies: [
    .package(url: "https://github.com/acme/analytics-ios", from: "2.1.0")
]

// Commit Package.resolved so CI checks out identical code.
// Reserve branch tracking for experiments only:
// .package(url: "...", branch: "main") // avoid in releases
📊 Production Insight
Committing Package.resolved ended works-on-my-Mac module errors. Rule: lint for committed resolved files and against branch tracking.
🎯 Key Takeaway
Pin from: or exact versions, commit Package.resolved, and lint against branch tracking on anything that ships or demos.

Platform Floors and Capability Mismatches

Platform requirements fail at dependency time with errors that masquerade as missing modules. A package declaring iOS 17 APIs can't link into a target deploying iOS 15, and the resulting diagnostic sometimes blames the module rather than the deployment gap. Read the full error: when it names platforms, the import name was never the problem.

Align the two numbers deliberately. Either raise your target's deployment setting to meet the package, accepting the dropped OS versions, or pin an older package release that still supports your floor. The package's README and release notes document supported platforms per version; match them before editing build settings.

Capabilities bite the same way. Packages vendoring resources, plugins, or binary targets need the corresponding Xcode version and sometimes explicit resource handling in the consuming target. A binary artifact built for simulators only fails linking for device archives in ways that read like absence. Check the package's platform and artifact matrix when the import is spelled right and linked yet still missing.

Record the floor decision next to the dependency. A comment in Package.swift or the readme stating the minimum iOS and the reason stops the next developer from upgrading past it blindly. Platform drift is silent until archive day; documentation makes it a reviewed choice instead of a release surprise.

📊 Production Insight
A deployment-floor gap read as a missing module until the full error was read. Rule: read platform diagnostics before respelling imports.
🎯 Key Takeaway
When the error names platforms, raise your deployment target or pin an older package release. Document the floor beside the dependency.
● Production incidentPOST-MORTEMseverity: high

The Widget That Missed the Demo Over One Link

Symptom
The archive for a partner demo failed with no such module in the widget extension, while the main app compiled cleanly. Re-adding the package and cleaning changed nothing because the project-level resolve was fine; only the widget target's link was missing.
Assumption
The team assumed adding the package once covered the whole project, since the app target built. Nobody checked the widget or test targets, and the branch-tracked dependency was treated as stable because it worked that morning. The demo device ran the app target, which masked the gap.
Root cause
The package product was linked only to the app target. The widget extension imported the same module but had no link entry, so its compile failed with no such module. The dependency also tracked a branch rather than a version, which meant a morning upstream commit had already moved the API the widget used.
Fix
The release engineer linked the analytics product to the widget and test targets, pinned the package to a from: version rule, and committed Package.resolved. CI gained per-target builds plus a branch-tracking lint, so unlinked targets and moving dependencies fail the pipeline instead of the demo.
Key lesson
  • Add-and-build-the-app doesn't prove the project builds. Compile every target, including widgets and tests, before calling a dependency done.
  • Branch-tracked dependencies are moving parts. Pin versions for anything that ships or demos, and reserve branches for experiments.
  • Commit Package.resolved and lint for it. Reproducible checkouts turn teammate-machine mysteries into local five-minute fixes.
Production debug guideFive checks that reconnect your target to the right package product.5 entries
Symptom · 01
No such module on import after adding the package
→
Fix
Select the target, open General, Frameworks, Libraries, and Embedded Content, and confirm the package product is listed. If missing, tap plus and add it. Rebuild that exact target before touching anything else.
Symptom · 02
Import name looks right but the module is missing
→
Fix
Open the package's Package.swift and read the products array, then compare with your import line. Fix the import to the product name and rebuild. Grep the codebase for the old wrong import to catch duplicates.
Symptom · 03
Module missing right after adding or upgrading a package
→
Fix
Run File, Packages, Resolve Package Versions and wait for badges to clear. Then run rm -rf ~/Library/Developer/Xcode/DerivedData/<project>-* and clean the build folder before rebuilding.
Symptom · 04
Module found yesterday, missing today, nothing changed
→
Fix
Quit Xcode completely, delete derived data, reopen the project, and let the package checkouts finish downloading before pressing build. Watch the navigator badges turn solid first.
Symptom · 05
Teammates see different modules from the same project
→
Fix
Open Package.resolved to confirm pinned versions, change branch tracking to a from: version rule, and commit the file. Run a clean CI build to prove every machine resolves identically.
Module Not Found Causes Compared
Root CauseHow to ConfirmFixPrevention
Package added to project but not linked to targetTarget's Frameworks list lacks the product; import fails with no such moduleAdd the package product to the target's Frameworks and LibrariesCheck target membership for every target that imports, including tests
Wrong import name: repo or target instead of productProduct list in Package.swift names a different importable moduleImport the product name exactly as declared in the packageCopy import names from Package.swift products, never from memory
Stale derived data or half-resolved checkoutPackage shows a warning badge; clean builds still fail after editsResolve versions, delete derived data, restart Xcode, rebuildResolve before building; never build while checkouts download
Branch tracking or version rule driftPackage.resolved missing or branch moved; teammates see different codePin exact or bounded versions and commit Package.resolvedUse from: version rules for releases; reserve branches for experiments
⚙ Quick Reference
4 commands from this guide
FileCommand / CodePurpose
check-links.shxcodebuild -listResolve Is Not Link
resolve-clean.shxcodebuild -resolvePackageDependencies -scheme ShopAppResolve Fully, Then Clean, Then Build
full-reset.shrm -rf ~/Library/Developer/Xcode/DerivedData/ShopApp-*Derived Data, Restarts, and Stale Module Graphs
Package.swiftdependencies: [Version Rules and Package.resolved That Reproduce

Key takeaways

1
Resolving downloads a package; linking attaches it to each target separately.
2
Import the product name from Package.swift, not the repo or target name.
3
Resolve versions fully before building; never compile mid-download.
4
Delete derived data and restart Xcode when the module graph goes stale.
5
Pin versions and commit Package.resolved for reproducible builds.
6
Link packages to test and widget targets wherever they import.

Common mistakes to avoid

5 patterns
×

Adding the package to the project but not to the target

Symptom
Module not found on import even though the package resolves. Other targets build while yours fails with no such module.
Fix
Open the target's General tab, Frameworks section, and confirm the package product is listed there. Add it explicitly for every target that imports the module, including tests and widgets.
×

Importing the repo name instead of the product name

Symptom
No such module for a name that looks right. The repository, target, and product names differ and you guessed the wrong one.
Fix
Import the product name from the package's products list, not the target or repo name. Check Package.swift or the package's product list in Xcode to confirm.
×

Building against a half-resolved package checkout

Symptom
Module not found right after adding, upgrading, or switching branches. The package shows in the navigator with a warning badge.
Fix
Resolve with File, Packages, Resolve Package Versions, then clean the build folder and rebuild. Confirm the checkout appears under the derived data source control folder.
×

Trusting stale derived data after package changes

Symptom
Module found yesterday, missing today, with no code changes. Cleaning alone doesn't help until derived data goes too.
Fix
Delete derived data, close Xcode fully, reopen, and let packages resolve before building. Don't build mid-resolution while checkouts are still downloading.
×

Tracking a branch instead of a versioned release

Symptom
Builds break randomly when the branch moves. Teammates on older checkouts see different modules than you do.
Fix
Pin an exact version or a bounded range like from 2.1.0, and commit Package.resolved. Avoid tracking branches for anything you release.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
A package is added but import fails. What's the first check?
Q02JUNIOR
How do package products differ from targets?
Q03SENIOR
Why does deleting derived data fix module errors?
Q04SENIOR
What's the correct resolve-clean-rebuild sequence?
Q05SENIOR
Why is branch tracking risky for release builds?
Q01 of 05JUNIOR

A package is added but import fails. What's the first check?

ANSWER
The package resolved at the project level but isn't linked to the building target. Open the target's Frameworks, Libraries, and Embedded Content section and add the package product there.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
Does adding a package once cover all targets?
02
How do I find the correct import name?
03
Is deleting derived data a real fix?
04
Why do tests fail to import a package the app uses?
05
Should Package.resolved be committed?
06
What if the package needs a newer iOS than my target?
N
Naren Founder & Principal Engineer

20+ years shipping production backend systems. Notes here come from systems that actually shipped.

Follow
✓ Verified
production tested
September 27, 2026
last updated
2,085
articles · all by Naren
🔥

That's Xcode. Mark it forged?

5 min read · try the examples if you haven't

←
Previous
Xcode Code Signing Error: No Profiles Found
2 / 2 · Xcode