Last updated 6 min read

Cursor for iOS Development – Installation Guide

Escaping Xcode’s monopoly is arguably one of the most significant advancements in iOS development in the past decade.
Cursor for iOS Development – Installation Guide
#iOS App Development

Escaping Xcode’s monopoly is arguably one of the most significant advancements in iOS development in the past decade. It’s especially meaningful that this was achieved through tools like VSCode and Cursor.

A massive shoutout to everyone who made this possible:

You are the ones driving the industry forward. Thank you for your incredible work!

1. Install Cursor

Download Cursor. The free tier should be enough to dive into the new era of coding experience.

2. Xcode build server

brew install xcode-build-server
SourceKit-LSP is an implementation of the Language Server Protocol (LSP) for Swift and C-based languages. It provides intelligent editor functionality like code-completion and jump-to-definition to editors that support LSP.

This package allows SourceKit-LSP to work outside of Xcode

Reference: GitHub - SolaWing/xcode-build-server: a build server protocol implementation for integrate xcode with sourcekit-lsp

3. XCBeautify

Pretty print the xcodebuild output within Cursor terminal.

brew install xcbeautify

Reference: GitHub - cpisciotta/xcbeautify: A little beautifier tool for xcodebuild

4. Swift Format

SwiftFormat is a library & CLI tool for reformatting Swift code.

brew install swiftformat

Reference: GitHub - nicklockwood/SwiftFormat: A command-line tool and Xcode Extension for formatting Swift code

5. Swift language support

Install Cursor extension:

  1. View -> Extensions -> Search "Swift language support"
  2. Install & Enable

This extension adds language support for Swift to Visual Studio Code with the following features:

  • Code completion
  • Jump to definition, peek definition, find all references, symbol search
  • Error annotations and apply suggestions from errors
  • Automatic generation of launch configurations for debugging with CodeLLDB
  • Automatic task creation
  • Package dependency view
  • Test Explorer view

Swift support uses SourceKit LSP for the language server to power code completion and LLDB to enable debugging.

6. Sweetpad

Basically, Sweetpad is a wrap-around xcodebuild CLI, and allowing access to targets, build schemes, destinations, manage simulators, run tests, etc. It can also debug and run tooling: Swift Format, SwiftLint, XcodeGen, etc.

  1. Install Cursor extension:

    • View -> Extensions -> Search "Sweetpad"
    • Install & Enable
  2. Initial integration with the build server:

    • CMD + SHIFT + P
    • Select: Sweetpad: Generate Build Server Config

This will create a buildServer.json at the root of your directory and allow Xcode Build Server to work with your project directory.

  1. Build & Run
    • CMD + SHIFT + P
    • Select: Sweetpad: Build & Run

From there, you can browse all your targets and hit run on any of them.

7. Hot Reloading

Cursor uses Sweetpad which uses xcodebuild to build and run the project. The problem is that it's slow: Why is xcodebuild slower than the Xcode GUI?.

Hot reloading helps.

I installed Inject lib via SPM and then followed the "Individual Dev Setup" steps:

  1. You must add "-Xlinker -interposable" (without the double quotes and on separate lines) to the "Other Linker Flags" of all targets in your project for the Debug configuration (qualified by the simulator SDK to avoid complications with bitcode), refer to InjectionForXcode documentation if you run into any issues
  2. Download the newest version of Xcode Injection from it's GitHub Page3. Unpack it and place it under /Applications
  3. Make sure that the Xcode version you are using to compile our projects is under the default location: /Applications/Xcode.app
  4. Run the injection application
  5. Select open project / open recent from its menu and pick the right workspace file you are using

Adding the following snippet should have made life with hot reloading even easier:

import SwiftUI
import Inject

extension View {
    func enableHotReloading() -> some View {
        modifier(HotReloading())
    }
}

struct HotReloading: ViewModifier {
    @ObserveInjection var inject
     
    func body(content: Content) -> some View {
        content
            .enableInjection()
    }
}

Unfortunately, it doesn't work as expected. With this enableHotReloading() modifier the view doesn't require a rebuild but still has to be manually refreshed. It can be triggered by tapping a button or hiding/showing the view.

The real hot reloading can only be achieved by placing an @ObserveInjection var inject in the view that is needed to hot reload.


struct ContentView: View {
    @ObserveInjection var inject
     
    var body: some View {
        content(...)
            .enableInjection()
    }
}

Troubleshooting

There are folks who are struggling to make it work with Cursor, and it seems like fixes came down only in the latest RC builds.

Anyway, in my case, it worked with Cursor out of the box with the following versions:

  • InjectIII v5.0.4
  • Inject: v1.5.2
  • Xcode v16.0.0

So I would suggest making things work with Xcode first, and only then move to Cursor.

8. Un-uglify Cursor Code Editor

For me personally, Cursor(VSCode) code editor looks like a plain notepad which makes the code look kinda ugly after Xcode.

Font Styling

It's because the Xcode editor adds a bit of bold styling to certain code definitions that make your code shine.

The config below allows you to reproduce it in Cursor:

  1. Open the settings file

    • CMD + SHIFT + P
    • Open User Settings JSON
  2. Paste the following config. It will add bold styling to certain definitions to make it look like in Xcode. It also removes vertical indentation lines.

"editor.guides.indentation": false,
"editor.tokenColorCustomizations": {
	"textMateRules": [
		{
			"scope": [
				"entity.name.type.struct.swift",
				"entity.name.type.class.swift",
				"entity.name.type.actor.swift",
				"entity.name.type.protocol.swift",
				"entity.name.type.enum.swift",
				"entity.name.type.swift"
			],
			"settings": {
				"fontStyle": "bold"
			}
		},
		
		{
			"scope": "meta.import.swift entity.name.type.swift",
			"settings": {
				"fontStyle": ""
			}
		}
	]
},

Xcode Theme

The whole thing would have made no sense if we hadn't installed the Xcode theme.

Cursor -> Extensions -> Search "Xcode Theme" and pick any.

/content/images/2024/11/Screenshot-2024-11-18-at-13.42.02.png

Common Pitfalls

Managing XCAssets

Basically, the XCAssets catalog is a bunch of directories with asset files and Contents.json configs with all properties. The problem is that VSCode/Cursor doesn't know how to handle them.

Solution: Use Xcode

The only solution I found is to keep an Xcode running and CMD + Tab into it when editing assets is needed.

Luckily, autocomplete for assets works fine in VSCode/Cursor.

Managing files of *.xcodeproj

When we add new files to the project Xcode makes sure that the *.xcodeproj is updated to include them in the right target and handle them properly depending on the file type.

Cursor/VSCode does not.

For now, it seems like Xcode is still the king for handling project configs.

Adding new *.swift files can be fixed in a few ways:

Option 1: Modularize the project with SPM packages

If the project itself is thin and all code is moved to local SPM packages there is no need to keep track of all files in *.xcodeproj.

Option 2: Use Tuist

Tuist makes the *.xcodeproj obsolete by generating it on the fly, based on the actual files.

Sweetpad has a setting that allows it to watch new .swift files and runs Tuist autogenerate.

Option 3: Use XcodeGen

The same solution as Tuist

Option 4: Use Xcodeproj Gem

Xcodeproj lets you create and modify Xcode projects from Ruby. Script boring management tasks or build Xcode-friendly libraries. Also includes support for Xcode workspaces (.xcworkspace), configuration files (.xcconfig), and Xcode Scheme files (.xcscheme).

So the solution could be:

  • Install Xcodeproj gem
  • Create a script that will run the Xcodeproj's add files command
  • Create VSCode task that will run the script

More details: [[Cursor. Script to Add Files to Xcode Project]]

Option 5: Use Xcode

Keep Xcode open and use CMD+Tab to switch to it whenever a new file needs to be created. Weird, but why not?


References