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

TrapSymptomFix
SwiftData model name collides with SwiftUI type'Tag' is ambiguous for type lookupAdd domain prefix (DocumentTag)
.gitkeep in Xcode 16 file-based projectMultiple 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.