home / skills / tddworks / claude-skills / ios-app-scaffold

ios-app-scaffold skill

/skills/ios-app-scaffold

This skill scaffolds iOS apps with Tuist and a layered Domain, Infrastructure, App architecture to accelerate project setup.

npx playbooks add skill tddworks/claude-skills --skill ios-app-scaffold

Review the files below or copy the command above to add this skill to your agents.

Files (3)
SKILL.md
4.7 KB
---
name: ios-app-scaffold
description: |
  Scaffold iOS apps with Tuist and layered architecture (Domain, Infrastructure, App).
  Use when: (1) Creating a new iOS app project, (2) Setting up Tuist project structure,
  (3) User asks to "create an iOS app", "scaffold an app", or "set up a new Swift project",
  (4) User wants layered/clean architecture for iOS, (5) User mentions Tuist setup.
---

# iOS App Scaffold

Scaffold iOS apps with Tuist, Swift 6, and layered architecture.

## Quick Start

Run the scaffold script:
```bash
python3 scripts/scaffold.py <AppName> <output-directory> [--bundle-id <id>] [--team-id <id>]
```

Example:
```bash
python3 scripts/scaffold.py MyApp /Users/me/projects --bundle-id com.mycompany.myapp --team-id ABC123
```

Then generate and open:
```bash
cd /Users/me/projects/MyApp
tuist generate
open MyApp.xcworkspace
```

## Generated Structure

```
AppName/
├── Sources/
│   ├── App/                    # SwiftUI views, app entry
│   │   ├── Views/
│   │   ├── Resources/
│   │   │   ├── Assets.xcassets
│   │   │   ├── XCConfig/       # Build configuration
│   │   │   │   ├── shared.xcconfig
│   │   │   │   ├── debug.xcconfig
│   │   │   │   └── release.xcconfig
│   │   │   └── en.lproj/
│   │   ├── Application/
│   │   ├── Info.plist
│   │   └── AppNameApp.swift
│   ├── Domain/                 # Business logic (no dependencies)
│   │   ├── Models/
│   │   ├── Protocols/          # @Mockable repository interfaces
│   │   └── Utils/
│   └── Infrastructure/         # Persistence implementations
│       └── Local/              # SwiftData repositories
├── Tests/
│   ├── DomainTests/
│   └── InfrastructureTests/
├── Project.swift               # Tuist configuration
├── Tuist.swift
├── .gitignore
└── README.md
```

## Architecture

| Layer | Purpose | Dependencies |
|-------|---------|--------------|
| **Domain** | Models, protocols, business logic | None |
| **Infrastructure** | SwiftData persistence | Domain |
| **App** | SwiftUI views, app entry | Domain, Infrastructure |

For detailed patterns, see [references/architecture.md](references/architecture.md).

## After Scaffolding

1. **Replace example files**: Edit `Example.swift`, `ExampleRepository.swift`, `LocalExampleRepository.swift`
2. **Add domain models**: Create models in `Sources/Domain/Models/`
3. **Define protocols**: Add repository protocols in `Sources/Domain/Protocols/`
4. **Implement persistence**: Add SwiftData entities in `Sources/Infrastructure/Local/`
5. **Build UI**: Create views in `Sources/App/Views/`

## Adding Dependencies

Edit `Project.swift` packages array:
```swift
packages: [
    .remote(url: "https://github.com/Kolos65/Mockable.git", requirement: .upToNextMajor(from: "0.5.0")),
    // Add more packages here
],
```

Then add to target dependencies:
```swift
dependencies: [
    .package(product: "PackageName"),
]
```

## Key Patterns

**Domain models are rich** - Include computed properties and business logic:
```swift
public struct Order: Identifiable, Codable, Sendable {
    public var items: [Item]
    public var total: Decimal { items.reduce(0) { $0 + $1.price } }
}
```

**Protocols use @Mockable** - Enables testing without real persistence:
```swift
@Mockable
public protocol OrderRepository: Sendable {
    func fetchAll() async throws -> [Order]
}
```

**Views consume Domain directly** - No ViewModel layer needed:
```swift
struct OrdersView: View {
    let orders: [Order]  // Domain model directly
}
```

## Version Management

Version numbers are managed in `Project.swift` build settings:

```swift
settings: .settings(
    base: [
        "MARKETING_VERSION": "1.0.0",      // App Store version
        "CURRENT_PROJECT_VERSION": "1",     // Build number
    ],
    ...
)
```

The `Info.plist` references these via build setting variables:
- `CFBundleShortVersionString` → `$(MARKETING_VERSION)`
- `CFBundleVersion` → `$(CURRENT_PROJECT_VERSION)`

To bump version, edit `Project.swift`:
```swift
"MARKETING_VERSION": "1.1.0",
"CURRENT_PROJECT_VERSION": "2",
```

## XCConfig

Build settings are managed via xcconfig files in `Sources/App/Resources/XCConfig/`:

| File | Purpose |
|------|---------|
| `shared.xcconfig` | Common settings (bundle ID, team ID, deployment target) |
| `debug.xcconfig` | Debug configuration (Apple Development signing) |
| `release.xcconfig` | Release configuration (Apple Development signing) |

Edit `shared.xcconfig` to customize:
```
PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.app
DEVELOPMENT_TEAM = YOUR_TEAM_ID
IPHONEOS_DEPLOYMENT_TARGET = 18.0
```

Overview

This skill scaffolds a new iOS app using Tuist, Swift 6, and a layered architecture (Domain, Infrastructure, App). It creates a ready-to-generate Xcode workspace, Swift package entries, xcconfig-based build settings, and example files to jumpstart development. Use it to enforce clean separation of concerns and accelerate project setup.

How this skill works

The scaffold script generates a Tuist Project.swift, Tuist.swift, and a Sources/ tree organized into Domain, Infrastructure, and App layers, plus Tests. It seeds example models, protocols annotated for @Mockable, SwiftData repository stubs, SwiftUI app entry points, and xcconfig files for signing and version management. After running the script you run tuist generate and open the workspace to start implementing features.

When to use it

  • Creating a brand new iOS app project with Tuist
  • Setting up a layered/clean architecture (Domain, Infrastructure, App)
  • Bootstrapping Swift 6 projects with SwiftData and SwiftUI examples
  • Initializing consistent build settings and versioning via xcconfig and Project.swift
  • When you want testable repository protocols with @Mockable support

Best practices

  • Replace and rename example files immediately (Example.swift, ExampleRepository.swift) to match your domain
  • Keep Domain layer business logic dependency-free and testable
  • Declare repository protocols in Domain and implement persistence in Infrastructure only
  • Manage app versions and build numbers in Project.swift, reference them in Info.plist via build settings
  • Add external Swift packages in Project.swift and wire them into target dependencies
  • Use xcconfig files (shared/debug/release) for signing, bundle id, and deployment target to keep configurations reproducible

Example use cases

  • Scaffold a new consumer app called MyApp with bundle and team IDs and immediately run tuist generate
  • Create a prototype where Domain models contain business rules and the Infrastructure layer uses SwiftData for persistence
  • Set up CI that bumps MARKETING_VERSION and CURRENT_PROJECT_VERSION in Project.swift before building
  • Start a test-driven workflow using @Mockable repository protocols and DomainTests
  • Standardize new internal projects with uniform xcconfig signing and deployment targets

FAQ

Do I need Tuist installed to use the scaffold?

Yes. Run the scaffold script to create the project, then use tuist generate to produce the workspace. Tuist must be installed and available on your PATH.

Where do I change the bundle id and team id?

Pass --bundle-id and --team-id to the scaffold script or edit Sources/App/Resources/XCConfig/shared.xcconfig to set PRODUCT_BUNDLE_IDENTIFIER and DEVELOPMENT_TEAM.

How do I add third-party packages?

Add package declarations to the packages array in Project.swift, then include the package product in the target dependencies section for the relevant target.