Skip to content

Latest commit

 

History

History
450 lines (326 loc) · 16.6 KB

File metadata and controls

450 lines (326 loc) · 16.6 KB

SwiftCatalyst

This project is a modern SwiftUI iOS template designed to make it easy to start building a project quickly using an AI coding tool like Cursor.

It follows the VIPER architecture to keep your code modular and scalable. The template includes built-in support for hot reloading in Cursor so you can see changes in the simulator as you work. It also comes with a set of custom Swift coding rules that guide style and best practices, including a meta-rule that helps you create new Cursor rules.

The project is pre-configured to work with Sweetpad for state previews, XcodeGen for project generation, SwiftLint for code quality enforcement, and InjectionNext for runtime code injection. This setup aims to make it easier to stay consistent, iterate quickly, and extend your codebase with AI assistance using Rules. Tested in Cursor, Windsurf, Trae, and VS Code.

Table of Contents

Features

  • VIPER Architecture: Clean, modular, and testable architecture pattern
  • SwiftUI: Modern declarative UI framework
  • Hot Reloading: Live UI updates without rebuilding
  • XcodeGen: Project generation to avoid merge conflicts
  • SwiftLint: Enforced code style and conventions
  • Async/Await: Modern concurrency handling
  • Environment Configuration: Customizable project settings through environment variables

Prerequisites

This project depends on several tools and environments. Please ensure the following are installed beforehand:


Setup Process

This will:

  • Create a real project suitable for production use
  • Customize project name, bundle IDs, and team ID
  • Set up multiple environments (development, staging, production)

Setup steps:

1. Clone the Repository

git clone https://github.com/danielraffel/SwiftCatalyst.git
cd SwiftCatalyst

2. Open in Cursor IDE

Open the project folder in Cursor IDE.


3. Create Your .env File

cp .env.example .env
nano .env

Edit the config:

# App Configuration
APP_NAME=YourAwesomeApp
BUNDLE_ID_PREFIX=com.yourcompany
APP_GROUP_ID=group.com.yourcompany.yourawesomeapp
TEAM_ID=ABCDEF1234

Save and exit nano:

  • Ctrl + O, then Enter to save
  • Ctrl + X to exit

See FAQ below if unsure about any of the values in this step.


4. Generate Project File

Run the included script to generate your project.yml from the template:

chmod +x generate-project.sh
./generate-project.sh

This will:

  • Generate a project.yml file with your custom values
  • Run XcodeGen to create the Xcode project
  • Inform you of the process with detailed logs

5. Install InjectionNext for Hot Reloading

  • Download InjectionNext:

    • Get the latest version from GitHub Releases
    • Download the InjectionNext.zip file
  • Install the App:

    • Unzip the downloaded file
    • Move InjectionNext.app to your Applications folder
    • Open the app (right-click and select "Open" if you encounter security warnings)
  • Start InjectionNext before running your project for hot reloading to work


6. Build and Run

  • In the InjectionNext (menu bar) app choose your target folder (eg ...or Watch Project) , then in Cursor clean and build to launch the simulator (e.g., Cmd+Shift+K to clean and Cmd+Shift+B to build).
InjectionNext Menu Bar

After building you should see this.

You’re now ready to start building! This is just a placeholder view—go ahead and replace it with / create your own custom VIPER module.


Hot Reloading with InjectionNext

Hot reloading is already configured in this template using the Inject framework (version >=1.5.0). The Cursor rules in this project should configure new views with the requirements described below.

Setting Up Hot Reloading

  • Start InjectionNext before running your project

Using Hot Reloading

To apply changes while the app is running in the simulator:

  1. Make changes to any SwiftUI view
  2. Save the file
  3. The changes will be immediately reflected in the simulator

Each SwiftUI view should:

  • Import the Inject framework
  • Include @ObserveInjection var inject property
  • Add .enableInjection() to the view body

Example:

import SwiftUI
import Inject

struct MyView: View {
    @ObserveInjection var inject
    
    var body: some View {
        Text("Hello, World!")
            .enableInjection()
    }
}

Project Structure

This repository contains a template project with all files at the root level:

.
├── .cursor/                              # Cursor IDE specific settings
│   └── rules/                            # Project coding rules
│       ├── cursor-rules-creation.mdc     # Guidelines for project-wide rule authoring
│       ├── git-commits.mdc               # Automating commits to git when succeeded with a requirement
│       └── swift-viper-architecture.mdc  # General Swift project conventions
├── .swiftlint.yml                        # SwiftLint configuration
├── .env.example                          # Environment variables template
├── Sources/                              # Application source code
│   ├── App.swift                         # SwiftUI App entry point
│   ├── Configuration/                    # Environment configuration
│   │   └── Configuration.swift           # Centralized environment variable handler
│   ├── Info.plist                        # App info property list
│   └── Modules/                          # VIPER modules
│       └── Home/                         # Custom module name (eg Home)
│           ├── View/                     # SwiftUI views
│           ├── Interactor/               # Business logic
│           ├── Presenter/                # Presentation logic
│           ├── Entity/                   # Data models (HomeEntity.swift)
│           └── Router/                   # Navigation logic
├── Tests/                                # Source code tests
│   ├── Info.plist                        # Test info property list
│   └── SwiftUIViperAppTests.swift        # Unit tests for Interactor and Presenter logic using XCTest
├── .gitignore                            # Git ignore file
├── generate-project.sh                   # Script for generating project file
├── project.yml.template                  # XcodeGen configuration template
└── README.md                             # Project documentation

VIPER Module Structure

Each module follows the VIPER architecture pattern:

  • View: SwiftUI view responsible for UI rendering
  • Interactor: Contains business logic and communicates with data sources
  • Presenter: Mediates between View and Interactor, prepares data for presentation
  • Entity: Data models used by the Interactor and Presenter
  • Router: Handles navigation and module creation

Creating a New VIPER Module

To create a new module, follow this structure:

  1. Create a new folder under Sources/Modules/
  2. Add the VIPER components (View, Interactor, Presenter, Entity, Router)
  3. Implement the module's functionality following the VIPER pattern

Customizing the Project

To rename the project or customize other aspects:

  1. Using Environment Configuration (Recommended)

    • Create a custom .env file based on .env.example
    • Run ./generate-project.sh to generate your project.yml with custom values
    • This approach allows you to easily configure:
      • Project name
      • Bundle IDs
      • Team ID
      • App Group ID
      • Other project-wide settings
  2. Manual Customization

    • Manually change the name property in project.yml (after creating it from the template)
    • Update bundleIdPrefix and other settings as needed
    • Regenerate the project using XcodeGen

Regardless of approach, consider:

  • Renaming the root module from Home to something more descriptive for your application
  • Updating module names to match your application's domain

Custom Keyboard Shortcuts in Cursor for Sweetpad

For an efficient development workflow in Cursor, configure these keyboard shortcuts to work better with Sweetpad:

  1. Open Cursor Settings:

    • Press Cmd+, (Command + Comma)
    • Select "Keyboard Shortcuts"
  2. Add these shortcuts:

  • Cmd+Shift+B → Build & run
  • Cmd+Shift+K → Clean build

Cursor Sweetpad Shortcuts


Acknowledgments


FAQ

📌 Where can I find my TEAM_ID, APP_GROUP_ID, and other Apple Developer values?

When setting up your .env file for the project, you'll need to supply values like APP_NAME, BUNDLE_ID_PREFIX, APP_GROUP_ID, and TEAM_ID.

Here's where to find each of them in your Apple Developer account:

APP_NAME

This is your app's display name—define it yourself in the ENV traditionally you would do that in:

  • Xcode: Target settings > General > Display Name
  • App Store Connect: "My Apps" > Select app

BUNDLE_ID_PREFIX

Typically in the form com.yourcompany, this is the custom portion of your app's bundle identifier.

You define it when you create the app's App ID in:

APP_GROUP_ID

Used for sharing data between app targets. Looks like: group.com.yourcompany.yourawesomeapp.

To create:

  1. Go to Identifiers
  2. Select your app or create one
  3. Enable App Groups and register a new one

Then add this group in Xcode under:

  • Signing & Capabilities > App Groups

TEAM_ID

Your 10-character Apple Team ID. Find it at:

.env Example

These values are then placed in your .env file like so:

APP_NAME=YourAwesomeApp
BUNDLE_ID_PREFIX=com.yourcompany
APP_GROUP_ID=group.com.yourcompany.yourawesomeapp
TEAM_ID=ABCDEF1234

👀 What It Looks Like

After completing setup and running the app, you should see a screen like this:

Simulator Screenshot

This shows:

  • A Home title from the default module
  • A welcome message and confirmation of hot reloading status
  • A Load Items button wired to the Presenter and Interactor
  • A list of example topics (Swift, UIKit, etc.) fetched via VIPER flow

You can customize this screen by modifying the Home module or adding new VIPER modules.


📄 What do the cursor rule files in this project do?

File Purpose
cursor-rules-creation.mdc A meta-rule that serves as a template/guide for creating new cursor rules specifically formatted for Swift and VIPER architecture.
git-commits.mdc Automated rule for creating standardized Git commits in conventional format for Swift projects and all related assets.
swift-viper-architecture.mdc Contains the project's Swift coding standards and implementation patterns for our VIPER architecture with hot reloading.

Using cursor-rules-creation.mdc (the meta-rule):

The meta-rule makes creating new rules simple:

  1. Notice a pattern you want to codify in your codebase
  2. Open a Cursor chat
  3. Point the AI to your meta-rule by saying:
    Using the @cursor-rules-creation.mdc guide...
    
  4. Ask it to write a new rule based on your conversation, for example:
    Write a rule for our networking layer implementation based on this chat
    
  5. Copy the generated rule content from the chat - Cursor won't save it automatically
  6. Create a new .mdc file in the .cursor/rules/ directory and paste the content
  7. Commit the new rule for team sharing

When should I create new rules?

Create new rules when:

  • Establishing patterns for new features or components
  • Setting conventions for specific areas of the codebase (like networking, data models, or UI components)
  • Documenting implementation requirements that you find yourself explaining repeatedly
  • Adding new architectural patterns or third-party integrations

Using git-commits.mdc:

The git commits rule automatically:

  1. Detects file changes after successful builds or edits
  2. Analyzes the change type based on file type and description
  3. Determines appropriate scope based on Swift/VIPER architecture patterns
  4. Formats commit messages according to conventional commits standard:
    feat(view): add user profile screen
    fix(networking): resolve API authentication issue
    refactor(presenter): extract presentation logic
    assets(media): add onboarding video
    
  5. Groups related files intelligently based on module structure

Using swift-viper-architecture.mdc:

  1. Automatic activation: These rules are automatically applied when editing matching Swift files.

  2. Manual activation: In a Cursor chat, you can reference this rule with:

    @swift-viper-architecture.mdc
    

Using Configuration in Code

The Configuration.swift file is included in the template and provides access to all environment variables:

// Example usage
let appName = Configuration.appName
let apiUrl = Configuration.apiBaseURL

You don't need to manually import the Configuration file in each of your files - it's available throughout the project.


✅ Recommended Project Locations for InjectionNext

To avoid permission issues, store your Xcode project outside of:

~/Desktop
~/Documents
~/Downloads
Any folder synced with iCloud Drive

Instead, use a location like:

~/Developer/
~/Projects/
~/Coding/
~/Code/

You can create a folder like ~/Developer/MyApp and move your project there.


License

This project is licensed under the MIT License - see the LICENSE file for details.