# When to Create a New Target Always-loaded core. Use the decision tree first. The DO/DON'T list and the checklist below cover the corner cases. ## TARGET CREATION DECISION TREE ``` Need to add new code? ├─ Is it a reusable domain model? │ └─ YES → Add to SharedModels │ ├─ Is it UI-related? │ ├─ Colors/semantic colors? → Add to AppColors │ ├─ Fonts/typography? → Add to AppFont │ ├─ Combined theme? → Add to AppTheme (uses AppColors + AppFont) │ ├─ Component system infrastructure? → Add to Components (core system) │ ├─ Hot reload support? → Add to SharedComponents │ ├─ Reusable app component? → Add to AppComponents │ └─ View helper/modifier? → Add to SharedViews │ ├─ Is it a complete user-facing feature? │ └─ YES → Create new *Feature target │ Example: ProfileFeature, SettingsFeature, PaymentFeature │ ├─ Is it API/networking related? │ ├─ Client-side? → Add to ApiClient (or create new client target) │ ├─ Server-side? → Add to ApiServer │ ├─ Shared DTOs? → Add to ApiShared (OpenAPI generated) │ └─ Middleware? → Create new *Middleware target │ ├─ Is it shared infrastructure? │ └─ YES → Create new Shared* target │ Example: SharedUtilities, SharedNetworking │ └─ Still unsure? └─ Ask: "Could this be reused in isolation?" ├─ YES → Create new target └─ NO → Add to most specific existing target ``` ## WHEN TO CREATE A NEW TARGET ### ✅ DO Create New Target When: 1. **New Feature Module** - Complete user-facing feature (login, profile, payments) - Example: `ProfileFeature`, `PaymentFlowFeature` 2. **Reusable Infrastructure** - Can be tested in isolation - Might be used by multiple features - Example: `CachingMiddleware`, `LoggingUtility` 3. **Third-Party Integration** - Wraps external library - Isolates external dependencies - Example: `ApplePayIntegration`, `BiometricAuth` 4. **Platform Separation** - Platform-specific code (iOS vs macOS) - Example: `IOSBiometrics`, `MacOSNotifications` 5. **Build Optimization** - Large, stable code that rarely changes - Expensive compilation (generated code) - Example: `ApiShared` (OpenAPI generated) ### ❌ DON'T Create New Target When: 1. **Single Use Case** - Only used by one feature - Tightly coupled to specific screen - → Add to that feature's target 2. **Trivial Helper** - 1-2 small functions - No external dependencies - → Add to existing utility target 3. **Temporary Code** - Proof of concept - Spike/experiment - → Keep in feature until proven stable ## DECISION CHECKLIST ### Before Creating a New Target - [ ] Target has single, clear responsibility - [ ] Target name follows conventions (*Feature, Shared*, Api*, *Components) - [ ] Dependencies are minimal and explicit - [ ] No circular dependencies introduced - [ ] Can be built and tested in isolation - [ ] Fits into architectural layer (Foundation/Infrastructure/Feature/App) - [ ] Test target created alongside source target - [ ] Product registered in Package.swift products array - [ ] Imports and declared dependencies point only to lower layers (no higher-layer dependencies) ### Before Adding to Existing Target - [ ] New code shares responsibility with the existing target - [ ] No better-suited target exists - [ ] Not creating a "god target" with mixed concerns - [ ] Won't introduce unwanted dependencies to target consumers ### Before Modifying Package.swift - [ ] Used closure-with-local-variables pattern for `deps` - [ ] Used closure-with-local-variables pattern for `allProducts` - [ ] Used closure-with-local-variables pattern for `targets` - [ ] NEVER used inline array definitions - [ ] Grouped dependencies by purpose (OpenAPI, Vapor, Apple-only, etc.) - [ ] Grouped products by platform (base vs. appleOnly) - [ ] Grouped targets by layer (Foundation, Infrastructure, Features) - [ ] Used comment headers (e.g., `// ---------- Shared Models ----------`) - [ ] In `Package.swift`: separated platform-specific code with `#if os(iOS) || os(macOS)` - [ ] In `Package.swift` only: did NOT use `#if canImport(UIKit)` (manifest evaluates it lazily and breaks Linux CI; `#if os()` is manifest-parse-time and safe). In regular Swift source files, `#if canImport(UIKit)` / `#if canImport(AppKit)` is the correct idiom for platform-conditional code. - [ ] Each target/product/dependency has descriptive variable name - [ ] Used trailing commas in all arrays - [ ] Applied `.when(platforms:)` for platform-specific dependencies within targets ### Before Adding Font/Resource Target - [ ] Used `.process()` for resources, NEVER `.copy()` - [ ] Created `FontRegistration.swift` with CoreText registration - [ ] Used `Bundle.module` for resource access, NEVER `Bundle.main` - [ ] Used `#if canImport(UIKit)` / `#if canImport(AppKit)` for platform imports - [ ] Filtered font files by extension (.otf, .ttf) - [ ] Used `CTFontManagerRegisterFontsForURL` with error handling - [ ] Added console logging for registration success/failure - [ ] Called `FontRegistration.registerFonts()` in app init BEFORE UI renders - [ ] NEVER used Info.plist font registration (`UIAppFonts`, `ATSApplicationFontsPath`) - [ ] Target has zero dependencies (fonts are Foundation layer) - [ ] Resources organized in dedicated subdirectory (e.g., `Fonts/`)