A comprehensive, modular Neovim plugin for Swift development with LSP, build tools, formatting, linting, and integrated LLDB debugger (no nvim-dap required).
- π Getting Started - Quick 3-step setup guide
- π Complete Documentation - Everything in one place
- β‘ Minimal Config - Simple setup (30 lines)
- π§ Full Config - All options (450 lines)
- Features
- Requirements
- Installation
- Quick Start
- Configuration
- Features Guide
- Commands Reference
- LuaLine Integration
- Examples
- Health Check
- Troubleshooting
- Contributing
- License
- π Smart Project Detection - Auto-detects SPM, Xcode projects, and workspaces
- π§ LSP Integration - Automatic sourcekit-lsp configuration with nvim-lspconfig
- π― Target Management - List, select, and display Swift targets in your statusline
- π¨ Build System - Build, run, and test Swift packages with live output
- π Code Formatting - Support for swift-format and swiftformat
- π Linting - SwiftLint integration with auto-fix
- π Debugger - Full debugging support with LLDB (no dependencies required)
- π Xcode Integration - Build schemes, list targets, open in Xcode.app
- β Version Validation - Validate Swift versions and tool compatibility
- π Health Checks - Comprehensive :checkhealth integration
- Neovim >= 0.8.0
- Swift toolchain - For development, building, and LSP
- nvim-lspconfig - For LSP support
- swiftly - Swift version manager (highly recommended)
- sourcekit-lsp - Comes with Swift toolchain or Xcode
- Xcode Command Line Tools - For Xcode project support (macOS)
- swift-format - Official Swift formatter from Apple
- swiftformat - Alternative Swift formatter
- SwiftLint - Swift linter for code quality
- nvim-cmp - For better code completions
- LuaLine - For statusline integration
# 1. Install swiftly (Swift version manager)
curl -L https://swift-server.github.io/swiftly/swiftly-install.sh | bash
# 2. Install Swift toolchain
swiftly install latest
# 3. Install formatters and linter (macOS)
brew install swift-format swiftformat swiftlint
# 4. Verify
swift --version
sourcekit-lsp --version
π¦ For detailed installation, see DEPENDENCIES.md
Using lazy.nvim (Recommended)
For LazyVim users, create ~/.config/nvim/lua/plugins/swift.lua
:
return {
{
"devswiftzone/swift.nvim",
ft = "swift",
opts = {
-- Your configuration here
},
},
}
For other lazy.nvim setups:
{
"devswiftzone/swift.nvim",
ft = "swift",
config = function()
require("swift").setup({
-- Your configuration here
})
end,
}
Using packer.nvim
use {
"devswiftzone/swift.nvim",
ft = "swift",
config = function()
require("swift").setup()
end,
}
Using vim-plug
Plug 'devswiftzone/swift.nvim'
lua << EOF
require("swift").setup()
EOF
{
dir = "~/projects/nvim/swift.nvim",
ft = "swift",
config = function()
require("swift").setup()
end,
}
Create ~/.config/nvim/lua/plugins/swift.lua
:
return {
{
"devswiftzone/swift.nvim",
ft = "swift",
opts = {}, -- Uses default configuration
},
}
# Restart Neovim or run:
:Lazy sync
cd your-swift-project
nvim Package.swift
# or
nvim Sources/main.swift
:checkhealth swift
You should see β marks for loaded features.
require("swift").setup({
enabled = true,
features = {
-- Project Detection
project_detector = {
enabled = true,
auto_detect = true, -- Auto-detect on buffer enter
show_notification = true, -- Show notification when project detected
cache_results = true, -- Cache detection results
},
-- LSP Integration
lsp = {
enabled = true,
auto_setup = true, -- Automatically setup LSP
sourcekit_path = nil, -- Auto-detect if nil
inlay_hints = true, -- Enable inlay hints
semantic_tokens = true, -- Enable semantic tokens
on_attach = nil, -- Custom on_attach function
capabilities = nil, -- Custom capabilities
cmd = nil, -- Custom command
root_dir = nil, -- Custom root_dir function
filetypes = { "swift" },
settings = {},
},
-- Target Manager
target_manager = {
enabled = true,
cache_timeout = 60, -- Cache targets for 60 seconds
},
-- Build Runner
build_runner = {
enabled = true,
auto_save = true, -- Save all files before building
show_output = true, -- Show output in split window
output_position = "botright", -- Position of output window
output_height = 15, -- Height of output window
close_on_success = false, -- Auto-close on successful build
focus_on_open = false, -- Focus output window when opened
},
-- Code Formatting
formatter = {
enabled = true,
tool = nil, -- Auto-detect: "swift-format" | "swiftformat"
format_on_save = false, -- Format on save
config_file = nil, -- Auto-detect
},
-- Linting
linter = {
enabled = true,
lint_on_save = true, -- Lint on save
auto_fix = false, -- Auto-fix issues
config_file = nil, -- Auto-detect
},
-- Xcode Integration
xcode = {
enabled = true,
default_scheme = nil, -- Default scheme to build
default_simulator = nil, -- Default simulator
show_output = true, -- Show build output
output_position = "botright",
output_height = 15,
},
},
log_level = "info",
})
require("swift").setup() -- Uses all defaults
require("swift").setup({
features = {
project_detector = {
show_notification = false, -- Disable popup notifications
},
},
})
require("swift").setup({
features = {
formatter = {
format_on_save = true,
tool = "swift-format", -- or "swiftformat"
},
},
})
require("swift").setup({
features = {
lsp = {
on_attach = function(client, bufnr)
-- Your custom keybindings
vim.keymap.set("n", "gd", vim.lsp.buf.definition, { buffer = bufnr })
vim.keymap.set("n", "K", vim.lsp.buf.hover, { buffer = bufnr })
end,
},
},
})
Automatically detects Swift projects in your workspace.
Supports:
- Swift Package Manager (Package.swift)
- Xcode Projects (*.xcodeproj)
- Xcode Workspaces (*.xcworkspace)
Commands:
:SwiftDetectProject " Manually detect project type
:SwiftProjectInfo " Show current project information
API:
local detector = require("swift.features.project_detector")
-- Check if we're in a Swift project
local is_project = detector.is_swift_project()
-- Get project root
local root = detector.get_project_root()
-- Get project type
local type = detector.get_project_type() -- "spm" | "xcode_project" | "xcode_workspace" | "none"
-- Get full project info
local info = detector.get_project_info()
-- Returns: { type = "spm", root = "/path", manifest = "/path/Package.swift", ... }
Configuration:
features = {
project_detector = {
enabled = true,
auto_detect = true,
show_notification = true,
cache_results = true,
},
}
Automatic configuration of sourcekit-lsp for full language server support.
Features:
- Auto-detection of sourcekit-lsp from Xcode or Swift toolchain
- Automatic LSP client setup with nvim-lspconfig
- Code completion, diagnostics, hover documentation
- Go to definition, find references, implementations
- Code actions and refactoring
- Inlay hints support
- Semantic tokens for better syntax highlighting
Default Keybindings:
gd
- Go to definitiongD
- Go to declarationK
- Hover documentationgi
- Go to implementationgr
- Find references<C-k>
- Signature help<leader>ca
- Code actions<leader>rn
- Rename symbol<leader>f
- Format document[d
/]d
- Previous/next diagnostic<leader>e
- Show diagnostic float<leader>q
- Diagnostics quickfix list
Configuration:
features = {
lsp = {
enabled = true,
auto_setup = true,
sourcekit_path = nil, -- Auto-detect
inlay_hints = true,
semantic_tokens = true,
on_attach = function(client, bufnr)
-- Your custom logic
end,
capabilities = nil,
settings = {},
},
}
Requirements:
- nvim-lspconfig
- sourcekit-lsp (comes with Xcode or Swift toolchain)
Detect and manage Swift targets from Package.swift and Xcode projects.
Features:
- Parse targets from Package.swift (executable, library, test)
- Extract schemes and targets from Xcode projects
- Select active target with interactive picker
- Statusline integration (see LuaLine section)
- Cached results for performance
Commands:
:SwiftTargets " List all available targets
:SwiftSelectTarget " Select target with interactive picker
:SwiftCurrentTarget " Show currently selected target
API:
local tm = require("swift.features.target_manager")
-- Get all targets
local targets = tm.get_targets()
-- Returns: { { name = "MyApp", type = "executable" }, ... }
-- Get target names only
local names = tm.get_target_names()
-- Returns: { "MyApp", "MyLibrary", "MyTests" }
-- Get executable targets only
local executables = tm.get_executable_targets()
-- Get/set current target
local current = tm.get_current_target()
tm.set_current_target("MyApp")
-- Get info for statusline
local info = tm.get_statusline_info()
-- Returns: { project_type = "spm", current_target = "MyApp", total_targets = 3 }
-- Get formatted parts for custom statusline
local parts = tm.get_lualine_parts()
-- Returns: { icon = "σ°₯", target = "MyApp", project = "MyProject", count = 3, text = "..." }
Configuration:
features = {
target_manager = {
enabled = true,
cache_timeout = 60, -- Cache targets for 60 seconds
},
}
Build, run, and test Swift Package Manager projects directly from Neovim.
Features:
- Build Swift packages with debug/release configurations
- Run Swift executables with custom arguments
- Execute tests with filtering support
- Clean build artifacts
- Live output in split window
- Auto-save before building
Commands:
:SwiftBuild [debug|release] " Build the Swift package
:SwiftRun [args] " Run the Swift package
:SwiftTest [args] " Run Swift tests
:SwiftClean " Clean build artifacts
:SwiftBuildClose " Close build output window
Examples:
:SwiftBuild " Build in debug mode
:SwiftBuild release " Build in release mode
:SwiftRun " Run the executable
:SwiftRun --help " Run with arguments
:SwiftTest " Run all tests
:SwiftTest MyTestSuite " Run specific test
Configuration:
features = {
build_runner = {
enabled = true,
auto_save = true, -- Save all files before building
show_output = true, -- Show output in split window
output_position = "botright", -- Position: botright, belowright, etc
output_height = 15, -- Height of output window
close_on_success = false, -- Auto-close on successful build
focus_on_open = false, -- Focus output window when opened
},
}
Keybindings Example:
keys = {
{ "<leader>sb", "<cmd>SwiftBuild<cr>", desc = "Swift build" },
{ "<leader>sr", "<cmd>SwiftRun<cr>", desc = "Swift run" },
{ "<leader>st", "<cmd>SwiftTest<cr>", desc = "Swift test" },
{ "<leader>sc", "<cmd>SwiftClean<cr>", desc = "Swift clean" },
}
Format Swift code using swift-format or swiftformat.
Features:
- Auto-detects swift-format and swiftformat
- Format on save option
- Format selection support
- Config file detection (.swift-format, .swiftformat)
Commands:
:SwiftFormat " Format current file
:SwiftFormatSelection " Format visual selection
Configuration:
features = {
formatter = {
enabled = true,
tool = nil, -- Auto-detect: "swift-format" | "swiftformat"
format_on_save = false, -- Enable to format on save
config_file = nil, -- Auto-detect .swift-format or .swiftformat
},
}
Format on Save:
features = {
formatter = {
format_on_save = true,
tool = "swift-format", -- Force specific formatter
},
}
SwiftLint integration with diagnostics and auto-fix.
Features:
- SwiftLint integration
- Lint on save with auto-fix option
- Diagnostic integration with LSP
- Config file detection (.swiftlint.yml)
Commands:
:SwiftLint " Lint current file
:SwiftLintFix " Auto-fix lint issues
Configuration:
features = {
linter = {
enabled = true,
lint_on_save = true, -- Lint automatically on save
auto_fix = false, -- Auto-fix issues on save
config_file = nil, -- Auto-detect .swiftlint.yml
},
}
Full debugging support for Swift using LLDB directly - no external dependencies required!
Features:
- Interactive debugging with breakpoints, stepping, and variable inspection
- Direct LLDB integration (no nvim-dap needed)
- Visual breakpoint indicators with custom signs
- Current line highlighting during debug sessions
- Build and debug workflow for both executables and tests
- Automatic detection of test targets (.xctest bundles)
- LLDB runs from project root with correct working directory
- Configurable debug output window (bottom, right, or floating)
- Send custom LLDB commands
- Debug both SPM packages and Xcode projects
Commands:
:SwiftDebug " Start debugging session
:SwiftBuildAndDebug " Build and start debugging
:SwiftBuildAndDebugTests " Build tests and start debugging (.xctest)
:SwiftDebugStop " Stop debugging session
:SwiftDebugContinue " Continue execution (F5)
:SwiftDebugStepOver " Step over (F10)
:SwiftDebugStepInto " Step into (F11)
:SwiftDebugStepOut " Step out (F12)
:SwiftBreakpointToggle " Toggle breakpoint at current line
:SwiftBreakpointClear " Clear all breakpoints
:SwiftDebugVariables " Show local variables
:SwiftDebugBacktrace " Show call stack
:SwiftDebugCommand <cmd> " Send custom LLDB command
:SwiftDebugUI " Toggle debug output window
Visual Indicators:
β
- Red breakpoint indicator in the sign columnβ€
- Blue current line indicator during debugging- Highlighted current line when stopped at a breakpoint
Examples:
" Toggle breakpoint at current line
:SwiftBreakpointToggle
" Build and start debugging an executable
:SwiftBuildAndDebug
" Build and debug tests (automatically detects .xctest bundles)
:SwiftBuildAndDebugTests
" Step through code
:SwiftDebugStepOver
" Inspect variables
:SwiftDebugVariables
" Send custom LLDB command
:SwiftDebugCommand p myVariable
" Show call stack
:SwiftDebugBacktrace
Configuration:
features = {
debugger = {
enabled = true,
lldb_path = nil, -- Auto-detect lldb
signs = {
breakpoint = "β", -- Breakpoint sign
current_line = "β€", -- Current line sign
},
colors = {
breakpoint = "DiagnosticError", -- Breakpoint color
current_line = "DiagnosticInfo", -- Current line color
},
window = {
position = "bottom", -- "bottom", "right", or "float"
size = 15, -- Height for bottom, width for right
},
},
}
Recommended Setup with Keybindings:
-- In your lazy.nvim configuration
return {
{
"devswiftzone/swift.nvim",
ft = "swift",
opts = {
features = {
debugger = {
enabled = true,
window = {
position = "bottom",
size = 15,
},
},
},
},
config = function(_, opts)
require("swift").setup(opts)
-- Debug keybindings
local debugger = require("swift.features.debugger")
vim.keymap.set("n", "<F5>", debugger.continue, { desc = "Debug: Continue" })
vim.keymap.set("n", "<F9>", debugger.toggle_breakpoint, { desc = "Debug: Toggle Breakpoint" })
vim.keymap.set("n", "<F10>", debugger.step_over, { desc = "Debug: Step Over" })
vim.keymap.set("n", "<F11>", debugger.step_into, { desc = "Debug: Step Into" })
vim.keymap.set("n", "<F12>", debugger.step_out, { desc = "Debug: Step Out" })
vim.keymap.set("n", "<leader>db", debugger.toggle_breakpoint, { desc = "Toggle Breakpoint" })
vim.keymap.set("n", "<leader>dc", debugger.continue, { desc = "Continue" })
vim.keymap.set("n", "<leader>ds", debugger.stop, { desc = "Stop Debugging" })
vim.keymap.set("n", "<leader>dv", debugger.show_variables, { desc = "Show Variables" })
vim.keymap.set("n", "<leader>dt", debugger.show_backtrace, { desc = "Show Backtrace" })
end,
},
}
Requirements:
- LLDB (included with Swift toolchain and Xcode on macOS)
- No additional plugins required!
Quick Start:
For debugging executables:
- Build your project:
:SwiftBuild
- Set breakpoints with
:SwiftBreakpointToggle
(or<F9>
) - Start debugging:
:SwiftBuildAndDebug
- Use F5/F10/F11/F12 to control execution
- View variables with
:SwiftDebugVariables
- Toggle debug output with
:SwiftDebugUI
For debugging tests:
- Select a test target with
:SwiftTarget
- Set breakpoints in your test files
- Start debugging tests:
:SwiftBuildAndDebugTests
- LLDB will automatically use the correct
.xctest
bundle
Build and manage Xcode projects from Neovim.
Features:
- Build Xcode projects with xcodebuild
- List and select schemes
- Open in Xcode.app
- Live build output
Commands:
:SwiftXcodeBuild [scheme] " Build Xcode project
:SwiftXcodeSchemes " List available schemes
:SwiftXcodeOpen " Open project in Xcode.app
Configuration:
features = {
xcode = {
enabled = true,
default_scheme = nil, -- Default scheme to build
default_simulator = nil, -- Default simulator
show_output = true,
output_position = "botright",
output_height = 15,
},
}
Note: Xcode integration requires macOS and Xcode Command Line Tools.
Validate Swift versions and tool compatibility.
Features:
- Check .swift-version file against installed Swift
- List swiftly installed versions
- Validate swift-format compatibility with Swift toolchain
- Detailed validation reports
Commands:
:SwiftValidateEnvironment " Full environment validation
:SwiftVersionInfo " Quick version information
Example Output:
ββββββββββββββββββββββββββββββββββββββββ
Swift Environment Validation
ββββββββββββββββββββββββββββββββββββββββ
β .swift-version file: /path/to/.swift-version
Required version: 6.2
β Installed Swift: 6.2.0
β Version matches requirement
β swiftly is available
Installed versions:
β 6.2.0
6.1.0
β swift-format is compatible
Swift: 6.2.0
swift-format: 6.2.0
ββββββββββββββββββββββββββββββββββββββββ
:SwiftInfo
- Show plugin information and configuration:SwiftValidateEnvironment
- Validate Swift environment:SwiftVersionInfo
- Show Swift version information
:SwiftDetectProject
- Detect and show Swift project type:SwiftProjectInfo
- Show current project information
:SwiftTargets
- List all Swift targets:SwiftSelectTarget
- Select target with picker:SwiftCurrentTarget
- Show current target
:SwiftBuild [debug|release]
- Build Swift package:SwiftRun [args]
- Run Swift package:SwiftTest [args]
- Run Swift tests:SwiftClean
- Clean build artifacts:SwiftBuildClose
- Close build output window
:SwiftFormat
- Format current file:SwiftFormatSelection
- Format selection:SwiftLint
- Lint current file:SwiftLintFix
- Auto-fix lint issues
:SwiftDebug
- Start debugging session:SwiftBuildAndDebug
- Build and start debugging:SwiftBuildAndDebugTests
- Build tests and start debugging (.xctest):SwiftDebugStop
- Stop debugging session:SwiftDebugContinue
- Continue execution:SwiftDebugStepOver
- Step over:SwiftDebugStepInto
- Step into:SwiftDebugStepOut
- Step out:SwiftBreakpointToggle
- Toggle breakpoint at current line:SwiftBreakpointClear
- Clear all breakpoints:SwiftDebugVariables
- Show local variables:SwiftDebugBacktrace
- Show call stack:SwiftDebugCommand <cmd>
- Send custom LLDB command:SwiftDebugUI
- Toggle debug output window
:SwiftXcodeBuild [scheme]
- Build Xcode project:SwiftXcodeSchemes
- List available schemes:SwiftXcodeOpen
- Open in Xcode.app
Display Swift targets in your statusline.
require("lualine").setup({
sections = {
lualine_x = {
{
function()
local ok, tm = pcall(require, "swift.features.target_manager")
if ok and vim.bo.filetype == "swift" then
return tm.statusline_simple()
end
return ""
end,
icon = "σ°₯",
color = { fg = "#ff6b00" }, -- Swift orange
},
"encoding",
"fileformat",
"filetype",
},
},
})
require("lualine").setup({
sections = {
lualine_x = {
{
function()
local ok, tm = pcall(require, "swift.features.target_manager")
if ok and vim.bo.filetype == "swift" then
return tm.statusline_detailed()
end
return ""
end,
color = { fg = "#ff6b00" },
},
"encoding",
"filetype",
},
},
})
require("lualine").setup({
sections = {
lualine_x = {
{
function()
local ok, tm = pcall(require, "swift.features.target_manager")
if not ok or vim.bo.filetype ~= "swift" then
return ""
end
local parts = tm.get_lualine_parts()
if not parts then
return ""
end
-- Customize how you display the parts
return string.format("%s %s", parts.icon, parts.target)
end,
color = { fg = "#ff6b00" },
},
"filetype",
},
},
})
For 10+ complete examples, see examples/lualine-integration.lua
See the examples/
directory for complete configuration examples:
- minimal-config.lua - Bare minimum setup
- lazyvim-config.lua - Full LazyVim integration
- local-dev-config.lua - Plugin development setup
- advanced-config.lua - Advanced usage with custom commands
- lualine-integration.lua - 10+ LuaLine statusline examples
Run :checkhealth swift
to verify the plugin is working correctly.
Checks:
- Plugin loaded successfully
- Configuration loaded
- All features status (enabled/disabled)
- Swift compiler installation
- Swift version and .swift-version file
- swiftly installation
- sourcekit-lsp availability
- swift-format/swiftformat compatibility
- SwiftLint installation
- Xcode tools (macOS)
- Target detection
Example:
:checkhealth swift
Expected Output:
swift.nvim
β Plugin loaded successfully
β Configuration loaded
Features
β Feature 'project_detector' is enabled
β Feature 'lsp' is enabled
β Feature 'target_manager' is enabled
...
Swift Compiler
β Swift compiler found
β Version: 6.2.0
Target Manager
β Target manager available
β Found 2 target(s)
β Current target: MyApp
β executable: 1
β test: 1
-
Check if installed:
:Lazy
-
Check for errors:
:messages
-
Reload plugin:
:Lazy reload swift.nvim
-
Make sure you have one of these files:
Package.swift
*.xcodeproj
*.xcworkspace
-
Try manual detection:
:SwiftDetectProject
-
Check filetype:
:set filetype?
Should show:
filetype=swift
-
Clear cache and refresh:
:lua vim.g.swift_current_target = nil :lua vim.b.swift_current_target = nil :SwiftTargets
-
Test
swift package dump-package
:cd your-project swift package dump-package
-
Check if sourcekit-lsp is available:
which sourcekit-lsp sourcekit-lsp --version
-
Check LSP status:
:LspInfo
-
Verify nvim-lspconfig is installed:
:lua print(vim.inspect(require("lspconfig")))
-
Run environment validation:
:SwiftValidateEnvironment
-
Install required Swift version:
swiftly install 6.2 swiftly use 6.2
-
Update tools to match Swift version:
brew upgrade swift-format
require("swift").setup({
log_level = "debug",
})
Then check messages:
:messages
Contributions are welcome! Please feel free to submit a Pull Request.
-
Clone the repository:
git clone https://github.com/devswiftzone/swift.nvim.git ~/projects/nvim/swift.nvim
-
Configure local plugin:
{ dir = "~/projects/nvim/swift.nvim", ft = "swift", config = function() require("swift").setup() end, }
-
Make changes and reload:
:Lazy reload swift.nvim
- Create feature file:
lua/swift/features/your_feature.lua
- Add configuration to
lua/swift/config.lua
- Load feature in
lua/swift/features/init.lua
- Add health check in
lua/swift/health.lua
- Update README and documentation
MIT License - see LICENSE file for details.
- Documentation: QUICKSTART.md | DEPENDENCIES.md | INSTALL.md
- Repository: https://github.com/devswiftzone/swift.nvim
- Issues: https://github.com/devswiftzone/swift.nvim/issues
- Swift: https://swift.org
- swiftly: https://github.com/swift-server/swiftly
Made with β€οΈ for the Swift community