What I Was Trying to Do
I started a new document scanning app and was setting up SwiftData models using TDD. Simple Document and Tag models:
@Model
class Tag {
var name: String
var color: String
@Relationship(inverse: \Document.tags)
var documents: [Document]
}
Built fine. No issues so far.
Trap #1: That’s Not Your Tag
The moment I used FetchDescriptor in a test file, it blew up:
let fetched = try context.fetch(FetchDescriptor<Tag>())
error: 'Tag' is ambiguous for type lookup in this context
Turns out SwiftUI already has a Tag type — the one used for identifying selection items in TabView and Picker. When your test file imports both @testable import scansort and SwiftUI (via SwiftData), the compiler can’t decide which Tag you mean.
What I Tried
First, I qualified it with the module name:
let fetched = try context.fetch(FetchDescriptor<scansort.Tag>())
Compiled, but having scansort.Tag scattered everywhere was ugly.
Then I tried a typealias:
typealias ScanSortTag = scansort.Tag
Put it in two test files — got invalid redeclaration since they’re in the same test module.
The Fix: Just Rename It
Renamed the model to DocumentTag:
@Model
class DocumentTag {
var name: String
var color: String
@Relationship(inverse: \Document.tags)
var documents: [Document]
}
Clean, no ambiguity:
let fetched = try context.fetch(FetchDescriptor<DocumentTag>())
Takeaway: When naming SwiftData models, check for collisions with SwiftUI and Foundation types. Names like Tag, Item, Group, and Label will almost certainly clash. Add a domain prefix.
Trap #2: .gitkeep Breaks the Build?
Same day, another surprise.
I created empty directories for the project structure — Models/, Views/, Services/ — and dropped .gitkeep files in them so Git would track the folders:
for dir in Models Services ViewModels Views/{Inbox,Archive,Scan}; do
touch "$dir/.gitkeep"
done
Build result:
error: Multiple commands produce '...scansort.app/.gitkeep'
The Cause: Xcode 16’s File-Based Projects
Starting with Xcode 16, new projects use objectVersion 77 — a file-based project format. The key change: every file in the source directory is automatically included in the target.
In older Xcode versions, files had to be explicitly registered in project.pbxproj. With objectVersion 77, the filesystem is the project structure. Drop a file in the source directory, and it’s part of the build.
So all 12 .gitkeep files got copied as bundle resources, and since they all share the same name, Xcode couldn’t resolve the conflict.
The Fix: Just Delete Them
find scansort -name ".gitkeep" -delete
In Xcode 16 file-based projects, .gitkeep is unnecessary. Xcode shows empty folders regardless, and Git will start tracking directories naturally once you add actual Swift files.
Takeaway: In Xcode 16 (objectVersion 77) projects, be careful about non-Swift files in your source directory. .gitkeep, .swiftlint.yml, and similar files can get included as build resources.
Summary
| Trap | Symptom | Fix |
|---|---|---|
| SwiftData model name collides with SwiftUI type | 'Tag' is ambiguous for type lookup | Add domain prefix (DocumentTag) |
.gitkeep in Xcode 16 file-based project | Multiple commands produce '.gitkeep' | Don’t use .gitkeep |
Both are “worked fine in the old project, breaks in the new one” type of issues. SwiftData and Xcode 16 quietly break some long-standing conventions, so it’s worth double-checking when you start a new project.