How to sync Cursor AI and Swift XCode projects (xcodeproj)
Introduction
Xcode uses .xcodeproj
files to manage everything. It tracks file references, groups, and build settings all in one place.
But the problem is that when you make changes outside of Xcode, such as in VSCode or with Cursor AI, those changes don't automatically reflect in the Xcode project.
You add a new Swift file in VSCode, and it’s like Xcode has no idea it exists. If you’re not careful, you can end up with mismatched files, missing references, or duplicate folders.
Let’s break down how you can keep everything in harmony, step by step.
1. Setting Up Your Swift Environment in Cursor AI
Installing Swift Extensions in Cursor
Swift Extension
First, make sure Swift support is fully set up in Cursor AI:
- Syntax highlighting and code completion
- Code navigation features
- Refactoring and quick fixes
- Package management support
- Debugging integration
SwiftLint
While not strictly necessary, this tool helps enforce Swift style and conventions.
brew install swiftlint
Install Swiftlint VS Code Extension
CodeLLDB
This extension is for debugging Swift code. It integrates with LLDB to provide a rich debugging experience.
Add a cursorrule file
With .cursorrules
we can give instructions to the AI.
This helps the AI to understand how we want our code and descriptions to be written.
Start with adding a .cursorrules
file to the root directory of your project.
Here is an example of what you can add:
you are an expert in coding with swift, swift ui. you always write maintainable code and clean code.
focus on latest september 2024 versions of the documentation and features.
your descriptions should be short and concise.
don't remove any comments.
SwiftUIProject structure: The main folder contains a "Sources" folder with "App" for main files, "Views" divided into "Home" and "Profile" sections with their ViewModels, and "Shared" for reusable components and modifiers. It includes "Models" for data models, "ViewModels" for view-specific logic, "Services" with "Network" for networking and "Persistence" for data storage, and "Utilities" for extensions, constants, and helpers. The "Resources" folder holds "Assets" for images and colors, "Localization" for localized strings, and "Fonts" for custom fonts. Lastly, the "Tests" folder includes "UnitTests" for unit testing and "UITests" for UI testing.
SwiftUI UI Design Rules:
Use Built-in Components: Utilize SwiftUI's native UI elements like List, NavigationView, TabView, and SF Symbols for a polished, iOS-consistent look.
Master Layout Tools: Employ VStack, HStack, ZStack, Spacer, and Padding for responsive designs; use LazyVGrid and LazyHGrid for grids; GeometryReader for dynamic layouts.
Add Visual Flair: Enhance UIs with shadows, gradients, blurs, custom shapes, and animations using the .animation() modifier for smooth transitions.
Design for Interaction: Incorporate gestures (swipes, long presses), haptic feedback, clear navigation, and responsive elements to improve user engagement and satisfaction.
Add swift documentation
Consider adding some custom swift documentation to your project. You can add any docs you like in cursor under
Cursor > Preferences > Cursor Settings > Features.
2. Install Sync and Code Generation libraries
As we learned in the previous section, XCode has its quirks. Add a file in Cursor? Rename something? Xcode ignores you.
Introducing XcodeGen
XcodeGen makes sure your project files stay in line with your actual folder structure. It's a real time-saver and keeps your workflow smooth.
Install XcodeGen
First things first, let's get XcodeGen installed. If you’ve got Homebrew, you’re set:
brew install xcodegen
Create your project.yml
This file is the heart of XcodeGen. It defines your project structure, targets, and settings. Here’s a basic example to get you going:
name: MyZunderApp
options:
bundleIdPrefix: com.zunderai
deploymentTarget:
iOS: 17.0
xcodeVersion: "15.3"
generateEmptyDirectories: true
createIntermediateGroups: true
targets:
MyZunderApp:
type: application
platform: iOS
sources: [MyZunderApp]
settings:
base:
SWIFT_VERSION: 5.10.1
ENABLE_TESTABILITY: YES
info:
path: Sources/Info.plist
properties:
CFBundleShortVersionString: "1.0.0"
CFBundleVersion: "1"
UILaunchStoryboardName: LaunchScreen
UIApplicationSceneManifest:
UIApplicationSupportsMultipleScenes: true
schemes:
MyZunderApp:
build:
targets:
MyZunderApp: all
run:
config: Debug
configs:
Debug: debug
Release: release
MyZunderApp
with your project name.Create the project dir
If the project.yml is like the following:
name: MyZunderApp
Then the project directory should be like the following:
MyZunderApp/
If you don't create this folder the xcodegen generate
command will not work.
mkdir MyZunderApp
Generate your XCode project
xcodegen generate
3. Automate Commands for a better workflow
In the next step we want to improve the workflow to reduce manual steps.
We will create a tasks.json
file and automate essential commands.
Like generating the XCode project, building the project or running Swiftlint.
So with this configuration we maintain consistency, and keep everything in sync without leaving your coding environment.
Create a new file in .vscode/tasks.json
and add the following:
{
"version": "2.0.0",
"tasks": [
{
"label": "Generate Xcode Project with XcodeGen",
"type": "shell",
"command": "xcodegen",
"args": [
"generate"
],
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": true,
"clear": false
},
"runOptions": {
"runOn": "folderOpen"
}
},
{
"label": "Build Swift Project",
"type": "shell",
"command": "xcodebuild",
"args": [
"-project",
"todoapp.xcodeproj",
"-scheme",
"MyZunderApp",
"-configuration",
"Debug",
"clean",
"build"
],
"problemMatcher": [
"$xcodebuild"
],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": true,
"clear": false
},
"runOptions": {
"runOn": "default"
}
}
]
}
-project
and -scheme
args with your project and scheme name. Look at the project.yml
file to find out what your project and scheme name is. "-project",
"todoapp.xcodeproj", # <-- replace this with your project name
"-scheme",
"MyZunderApp", # <-- replace this with your scheme name
Run tasks with Cursor AI
Now you can run the tasks with Cursor AI.
Hit (Cmd+Shift+B
or Ctrl+Shift+B
) and select the task you want to run.
Bonus: Use swiftlint to keep your code clean
Install Swiftlint
If you haven't already installed swiftlint.
brew install swiftlint
Add a Swiftlint configuration file
In your project directory, create a .swiftlint.yml
file to customize SwiftLint rules according to your needs:
disabled_rules:
- line_length # Disable if long lines are frequently unavoidable
- trailing_whitespace # Manage this rule based on team preferences
- file_length # Disable for larger files like data models
- cyclomatic_complexity # Disable if complex functions are unavoidable
- force_cast # Be cautious but allow force casting where necessary
- force_unwrapping # Allow force unwrapping for simplicity in some cases
- large_tuple # Manage tuple size based on specific needs
- function_body_length # Disable if longer functions are necessary
- type_body_length # Disable if types naturally grow large
opt_in_rules:
- empty_count # Prefer `.isEmpty` over counting elements
- closure_end_indentation # Align closing brackets for better readability
- sorted_first_last # Use `.first` and `.last` instead of `.sorted()`
- strict_fileprivate # Enforce using `fileprivate` when appropriate
- redundant_nil_coalescing # Avoid unnecessary nil coalescing
included:
- Sources
- Tests
excluded:
- Carthage
- Pods
- .build
- Generated
reporter: "xcode"
Integrate Swiftlint to Cursor AI
Add this to your .vscode/tasks.json
file:
{
"label": "Run SwiftLint",
"type": "shell",
"command": "swiftlint",
"args": [],
"problemMatcher": [
"$swiftlint"
],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": true,
"clear": false
},
"runOptions": {
"runOn": "folderOpen"
}
}
Now you can run the Swiftlint command from the command palette in Cursor.
Hit (Cmd+Shift+B or Ctrl+Shift+B) and select "Run Task" > "Run SwiftLint".
More resources
Swiftenv
swiftenv is a tool to easily manage and switch between multiple Swift versions on your system.
Happy coding! 🎉
Ultimate Cursor AI Course is now available!
Watch free lessons and secure your early bird spot.
Watch Now