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.
- 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
This project depends on several tools and environments. Please ensure the following are installed beforehand:
- Xcode 16.0+
- Cursor IDE
- Sweetpad extension for Cursor
- XcodeGen (
brew install xcodegen) - SwiftLint (
brew install swiftlint) - InjectionNext - setup steps included below
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:
git clone https://github.com/danielraffel/SwiftCatalyst.git
cd SwiftCatalystOpen the project folder in Cursor IDE.
cp .env.example .env
nano .envEdit 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.
Run the included script to generate your project.yml from the template:
chmod +x generate-project.sh
./generate-project.shThis will:
- Generate a
project.ymlfile with your custom values - Run XcodeGen to create the Xcode project
- Inform you of the process with detailed logs
-
Download InjectionNext:
- Get the latest version from GitHub Releases
- Download the
InjectionNext.zipfile
-
Install the App:
- Unzip the downloaded file
- Move
InjectionNext.appto 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
- 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+Kto clean andCmd+Shift+Bto build).
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 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.
- Start InjectionNext before running your project
To apply changes while the app is running in the simulator:
- Make changes to any SwiftUI view
- Save the file
- The changes will be immediately reflected in the simulator
Each SwiftUI view should:
- Import the Inject framework
- Include
@ObserveInjection var injectproperty - 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()
}
}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
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
To create a new module, follow this structure:
- Create a new folder under
Sources/Modules/ - Add the VIPER components (View, Interactor, Presenter, Entity, Router)
- Implement the module's functionality following the VIPER pattern
To rename the project or customize other aspects:
-
Using Environment Configuration (Recommended)
- Create a custom
.envfile based on.env.example - Run
./generate-project.shto 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
- Create a custom
-
Manual Customization
- Manually change the
nameproperty inproject.yml(after creating it from the template) - Update
bundleIdPrefixand other settings as needed - Regenerate the project using XcodeGen
- Manually change the
Regardless of approach, consider:
- Renaming the root module from
Hometo something more descriptive for your application - Updating module names to match your application's domain
For an efficient development workflow in Cursor, configure these keyboard shortcuts to work better with Sweetpad:
-
Open Cursor Settings:
- Press
Cmd+,(Command + Comma) - Select "Keyboard Shortcuts"
- Press
-
Add these shortcuts:
Cmd+Shift+B→ Build & runCmd+Shift+K→ Clean build
- XcodeGen - Xcode project generation
- Sweetpad - Cursor IDE integration
- SwiftLint - A tool to enforce Swift style and conventions
- Inject - Hot reloading support
- InjectionNext - Runtime code injection
- The Cursor Rules system implemented in this project:
- cursor-rules-creation.mdc was inspired by Adithyan and Geoffrey Huntley
- git-commits.mdc was inspired by Geoffrey Huntley
- swift-viper-architecture.mdc was inspired by Swift Cursor Rules by Ray Fernando & Lou Zell.
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:
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
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:
- Certificates, Identifiers & Profiles > Identifiers
Used for sharing data between app targets. Looks like: group.com.yourcompany.yourawesomeapp.
To create:
- Go to Identifiers
- Select your app or create one
- Enable App Groups and register a new one
Then add this group in Xcode under:
- Signing & Capabilities > App Groups
Your 10-character Apple Team ID. Find it at:
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=ABCDEF1234After completing setup and running the app, you should see a screen like this:
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. |
The meta-rule makes creating new rules simple:
- Notice a pattern you want to codify in your codebase
- Open a Cursor chat
- Point the AI to your meta-rule by saying:
Using the @cursor-rules-creation.mdc guide... - 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 - Copy the generated rule content from the chat - Cursor won't save it automatically
- Create a new
.mdcfile in the.cursor/rules/directory and paste the content - Commit the new rule for team sharing
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
The git commits rule automatically:
- Detects file changes after successful builds or edits
- Analyzes the change type based on file type and description
- Determines appropriate scope based on Swift/VIPER architecture patterns
- 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 - Groups related files intelligently based on module structure
-
Automatic activation: These rules are automatically applied when editing matching Swift files.
-
Manual activation: In a Cursor chat, you can reference this rule with:
@swift-viper-architecture.mdc
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.apiBaseURLYou don't need to manually import the Configuration file in each of your files - it's available throughout the project.
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.
This project is licensed under the MIT License - see the LICENSE file for details.
