Adopt the rules-swift pin-and-codify strategy in snapshot mode (third_party/rules-swift @97dc70f + sha256), add codified gates, a conformance audit ledger, and .github/workflows/rules-swift.yml (corpus-pin integrity required, full gate report mode). Remove the docs/rules mirror it supersedes (repo-specific files kept under docs/). Style and leak gates exclude the vendored corpus.
6.2 KiB
Common Mistakes to Avoid
Anti-patterns paired with the canonical fix. Mini-DOs are inline; full canonical forms live in package-swift.md (closure-with-locals), testing.md (test-target placement), and implementation-patterns.md (Pattern 6 for fonts).
❌ DON'T: Create God Packages
// WRONG: "Shared" package with everything
Packages/Sources/Shared/
├── Models/
├── Views/
├── Networking/
├── Database/
└── Utilities/
✅ DO: Split into focused packages
Packages/Sources/
├── SharedModels/
├── SharedViews/
├── NetworkClient/
├── DatabaseClient/
└── SharedUtilities/
❌ DON'T: Circular Dependencies
// WRONG
AuthFeature depends on AppFeature
AppFeature depends on AuthFeature
✅ DO: Extract shared code
AuthFeature depends on SharedAuthModels
AppFeature depends on SharedAuthModels
❌ DON'T: Inline deps, allProducts, or targets arrays
// WRONG: inline definitions
let deps: [Package.Dependency] = [
.package(url: "...", from: "1.10.3"),
.package(url: "...", from: "1.8.3"),
// ... dozens more
]
let allProducts: [Product] = [
.library(name: "ApiShared", targets: ["ApiShared"]),
.library(name: "ApiClient", targets: ["ApiClient"]),
// ... many more
]
let targets: [Target] = [
Target.target(name: "SharedModels", dependencies: []),
Target.testTarget(name: "SharedModelsTests", dependencies: ["SharedModels"]),
// ... more inline
]
✅ DO: closure-with-local-variables pattern, grouped by purpose / layer / platform. Mini-shape for each array:
// deps
let deps: [Package.Dependency] = {
// OpenAPI stack
let openAPIRuntimeDep = Package.Dependency.package(url: "...", from: "1.8.3")
// Vapor stack
let vaporDep = Package.Dependency.package(url: "...", from: "4.119.0")
return [openAPIRuntimeDep, vaporDep]
}()
// allProducts
let allProducts: [Product] = {
let apiSharedProduct = Product.singleTargetLibrary("ApiShared")
let apiClientProduct = Product.singleTargetLibrary("ApiClient")
return [apiSharedProduct, apiClientProduct]
}()
// targets
let targets: [Target] = {
// foundation
let sharedModelsTarget = Target.target(name: "SharedModels", dependencies: [])
let foundationTargets = [sharedModelsTarget]
// api
let apiClientTarget = Target.target(name: "ApiClient", dependencies: ["SharedModels"])
let apiTargets = [apiClientTarget]
return foundationTargets + apiTargets
}()
Full forms (with platform splits, helper extensions, every dep / product / target spelled out) in package-swift.md (Dependencies Definition, Product Definition, Target Organization).
❌ DON'T: Skip Test Targets
Every package MUST ship with a matching Target.testTarget(name: "<name>Tests", dependencies: ["<name>"]), grouped as a pair with the source target in a named sub-array. See testing.md > TEST TARGET PLACEMENT for the canonical form, folder layout, and the rule that mocks live in the test target rather than the source package.
❌ DON'T: Transitive Dependencies
// WRONG: Relying on ApiClient importing SharedModels
import ApiClient
// Using User from SharedModels without importing it
✅ DO: Explicit imports
import ApiClient
import SharedModels // Explicit dependency
❌ DON'T: Feature-to-Feature Dependencies
// WRONG: Features depending on each other
let appFeatureTarget = Target.target(
name: "AppFeature",
dependencies: [
"AuthFeature", // ❌ Feature depending on feature
"ProfileFeature", // ❌ Creates tight coupling
]
)
✅ DO: Coordinator pattern or shared protocols
// Extract navigation/coordination to AppFeature (parent)
// Child features (Auth, Profile) depend on parent's protocols
let authFeatureTarget = Target.target(
name: "AuthFeature",
dependencies: [
"SharedModels",
"AppCoordination", // ✅ Protocol package
]
)
❌ DON'T: Use Info.plist for Font Registration in Packages
Three anti-patterns, all covered by implementation-patterns.md > Pattern 6 (Font/Resource Package):
UIAppFonts/ATSApplicationFontsPathin Info.plist: doesn't work in SPM packages.copy("Fonts")inPackage.swiftresources: breaksBundle.modulelookupBundle.main.urls(...)inside the package: wrong bundle for SPM resources
DO: .process("Fonts") in resources, CTFontManagerRegisterFontsForURL against Bundle.module, called from app init before any UI renders. Full implementation in implementation-patterns.md > Pattern 6.
Acceptance check
Each anti-pattern above is mechanically checkable; the fixes are conforming when:
- No inline top-level arrays. The three top-level manifest arrays use the closure-with-local-variables form, never a bare array literal:
grep -nE 'let (deps|allProducts|targets)[^=]*=[[:space:]]*\[' Packages/Package.swift Package.swift
Output MUST be empty (each should read let deps: [...] = {), per the closure-with-locals fix.
- No Info.plist font registration in packages. Packages register fonts via CoreText, never the plist keys or
.copy("Fonts"):
grep -rnE 'UIAppFonts|ATSApplicationFontsPath|\.copy\("Fonts"\)' Packages/
Output MUST be empty (use .process("Fonts") + CTFontManagerRegisterFontsForURL against Bundle.module, Pattern 6).
- Every source target has a matching test target. For each
Target.target(name: "X")there is aTarget.testTarget(name: "XTests"). List the names and confirm no source target lacks its pair:
grep -oE 'Target\.(target|testTarget)\(name: "[^"]+"' Packages/Package.swift
Every non-test target name MUST have a corresponding <name>Tests.
- Observable checklist for the structural anti-patterns no grep settles, each a clear pass/fail: no "God package" mixing unrelated concerns (Models, Views, Networking, Database under one target); no circular dependency between two packages (the graph is a DAG); no feature target lists another feature target as a dependency (coordination goes through a parent/protocol package); and every cross-package symbol use has an explicit
importof the package that declares it, never a reliance on a transitive import.