-
-
Notifications
You must be signed in to change notification settings - Fork 19
Integrations
Xcodebuild.nvim is integrated with nvim-tree, neo-tree, and oil.nvim to let you manage your project and files in a convenient way.
Every change in the file tree presented by these plugins will be automatically reflected in the Xcode project file.
Additionally, the Project Manager will try predicting targets for newly created files based on their location.
If you prefer to select targets manually, you can always disable it in the configuration using
integrations.nvim_tree.guess_target
or integrations.oil_nvim.guess_target
.
nvim-tree.mp4
nvim-dap plugin lets you debug applications like in any other IDE. On top of that nvim-dap-ui extension will present for you all panels with stack, breakpoints, variables, logs, etc.
To configure DAP for development:
- Download codelldb VS Code plugin from: HERE. For macOS use
darwin
version. - Unzip
vsix
file and set paths in the configuration below. - Install also nvim-dap-ui for a nice GUI to debug.
- Make sure to enable
console
window fromnvim-dap-ui
to see simulator logs.
return {
"mfussenegger/nvim-dap",
dependencies = {
"wojciech-kulik/xcodebuild.nvim"
},
config = function()
local xcodebuild = require("xcodebuild.integrations.dap")
-- SAMPLE PATH, change it to your local codelldb path
local codelldbPath = os.getenv("HOME") .. "/tools/codelldb-aarch64-darwin/extension/adapter/codelldb"
xcodebuild.setup(codelldbPath)
vim.keymap.set("n", "<leader>dd", xcodebuild.build_and_debug, { desc = "Build & Debug" })
vim.keymap.set("n", "<leader>dr", xcodebuild.debug_without_build, { desc = "Debug Without Building" })
vim.keymap.set("n", "<leader>dt", xcodebuild.debug_tests, { desc = "Debug Tests" })
vim.keymap.set("n", "<leader>dT", xcodebuild.debug_class_tests, { desc = "Debug Class Tests" })
vim.keymap.set("n", "<leader>b", xcodebuild.toggle_breakpoint, { desc = "Toggle Breakpoint" })
vim.keymap.set("n", "<leader>B", xcodebuild.toggle_message_breakpoint, { desc = "Toggle Message Breakpoint" })
vim.keymap.set("n", "<leader>dx", xcodebuild.terminate_session, { desc = "Terminate Debugger" })
end,
}
Since iOS 17, a new secure connection between Mac and mobile devices is required.
Xcodebuild.nvim uses pymobiledevice3 to establish a special trusted tunnel that
is later used for debugging. However, this operation requires sudo
(more details).
Showing a pop-up to enter a password every time you run a debugger would be quite annoying.
That's why the plugin provides a small script (tools/remote_debugger)
that wraps the only two operations requiring sudo
(starting a secure tunnel and closing it).
This allows you to configure passwordless access just for this single file and make it work with xcodebuild.nvim.
Caution
Giving passwordless sudo access to that file, potentially opens a gate for malicious software
that could modify the file and run some evil code using root
account. The best way to protect
that file is to create a local copy, change the owner to root
, and give write permission only
to root
. The same must be applied to the parent directory. The script below automatically secures
the file.
👉 Enable integration
Update your config with:
integrations = {
pymobiledevice = {
enabled = true,
},
}
👉 Run the following command to install & protect the script
DEST="$HOME/Library/xcodebuild.nvim" && \
SOURCE="$HOME/.local/share/nvim/lazy/xcodebuild.nvim/tools/remote_debugger" && \
ME="$(whoami)" && \
sudo install -d -m 755 -o root "$DEST" && \
sudo install -m 755 -o root "$SOURCE" "$DEST" && \
sudo bash -c "echo \"$ME ALL = (ALL) NOPASSWD: $DEST/remote_debugger\" >> /etc/sudoers"
Logs without using nvim-dap
If you don't want to use nvim-dap you can always print logs directly to your terminal by calling (from your root directory):
tail -f .nvim/xcodebuild/app_logs.log
This approach works especially well if you are using tmux.
If you like to have a nice panel with issues you can try Trouble plugin. Xcodebuild.nvim adds all errors and warnings to the quickfix list, so you can just open Trouble in quickfix
mode and you should be able to see all issues.
You can also configure it to appear automatically on build/test failure:
vim.api.nvim_create_autocmd("User", {
pattern = { "XcodebuildBuildFinished", "XcodebuildTestsFinished" },
callback = function(event)
if event.data.cancelled then
return
end
if event.data.success then
require("trouble").close()
elseif not event.data.failedCount or event.data.failedCount > 0 then
if next(vim.fn.getqflist()) then
require("trouble").open("quickfix")
else
require("trouble").close()
end
require("trouble").refresh()
end
end,
})
Plugin config:
return {
"folke/trouble.nvim",
dependencies = { "nvim-tree/nvim-web-devicons" },
event = { "BufReadPre", "BufNewFile" },
keys = {
{ "<leader>tt", "<cmd>Trouble quickfix toggle<cr>", { desc = "Open a quickfix" } },
},
opts = {},
config = function()
require("trouble").setup({
auto_open = false,
auto_close = false,
auto_preview = true,
auto_jump = false,
mode = "quickfix",
severity = vim.diagnostic.severity.ERROR,
cycle_results = false,
})
end,
}
Additionally, you can add extra bindings to easily jump between issues across the whole project:
vim.keymap.set("n", "<A-d>", "<cmd>silent cc | silent cn<cr>zz", { desc = "Jump to next issue" })
vim.keymap.set("n", "<A-s>", "<cmd>silent cc | silent cp<cr>zz", { desc = "Jump to previous issue" })
You can also integrate this plugin with lualine.nvim.
local function xcodebuild_device()
if vim.g.xcodebuild_platform == "macOS" then
return " macOS"
end
local deviceIcon = ""
if vim.g.xcodebuild_platform:match("watch") then
deviceIcon = ""
elseif vim.g.xcodebuild_platform:match("tv") then
deviceIcon = " "
elseif vim.g.xcodebuild_platform:match("vision") then
deviceIcon = " "
end
if vim.g.xcodebuild_os then
return deviceIcon .. " " .. vim.g.xcodebuild_device_name .. " (" .. vim.g.xcodebuild_os .. ")"
end
return deviceIcon .. " " .. vim.g.xcodebuild_device_name
end
------
lualine_x = {
{ "' ' .. vim.g.xcodebuild_last_status", color = { fg = "Gray" } },
{ "' ' .. vim.g.xcodebuild_test_plan", color = { fg = "#a6e3a1", bg = "#161622" } },
{ xcodebuild_device, color = { fg = "#f9e2af", bg = "#161622" } },
},
Global variables that you can use:
Variable | Description |
---|---|
vim.g.xcodebuild_device_name |
Device name (ex. iPhone 15 Pro) |
vim.g.xcodebuild_os |
OS version (ex. 16.4) |
vim.g.xcodebuild_platform |
Device platform (ex. iPhone Simulator) |
vim.g.xcodebuild_scheme |
Selected project scheme (ex. MyApp) |
vim.g.xcodebuild_test_plan |
Selected Test Plan (ex. MyAppTests) |
vim.g.xcodebuild_last_status |
Last build/test status (ex. Build Succeeded [15s]) |
Below you can find a sample configuration that intercepts xcodebuild.nvim events and sends them to fidget.nvim.
return {
"j-hui/fidget.nvim",
event = "VeryLazy",
config = function()
local fidget = require("fidget")
fidget.setup({
notification = {
window = {
normal_hl = "String", -- Base highlight group in the notification window
winblend = 0, -- Background color opacity in the notification window
border = "rounded", -- Border around the notification window
zindex = 45, -- Stacking priority of the notification window
max_width = 0, -- Maximum width of the notification window
max_height = 0, -- Maximum height of the notification window
x_padding = 1, -- Padding from right edge of window boundary
y_padding = 1, -- Padding from bottom edge of window boundary
align = "bottom", -- How to align the notification window
relative = "editor", -- What the notification window position is relative to
},
},
})
end,
}
Top of the file with xcodebuild.nvim config:
local progress_handle
xcodebuild.nvim setup options:
show_build_progress_bar = false,
logs = {
notify = function(message, severity)
local fidget = require("fidget")
if progress_handle then
progress_handle.message = message
if not message:find("Loading") then
progress_handle:finish()
progress_handle = nil
if vim.trim(message) ~= "" then
fidget.notify(message, severity)
end
end
else
fidget.notify(message, severity)
end
end,
notify_progress = function(message)
local progress = require("fidget.progress")
if progress_handle then
progress_handle.title = ""
progress_handle.message = message
else
progress_handle = progress.handle.create({
message = message,
lsp_client = { name = "xcodebuild.nvim" },
})
end
end,
},
Demo:
demo.mov
Tip
Key messages like build/test finished are published as notifications, so if you miss one, you can check out :Fidget history
.
If you installed nvim-dap and nvim-dap-ui, you can easily track your app logs. The plugin automatically
sends logs to the console
window provided by nvim-dap-ui.
To see SIMULATOR logs you don't need to run the debugger. You can just show the console
and run the app
(remember that the app must be launched using xcodebuild.nvim).
:lua require("dapui").toggle()
Important
Logs printed by NSLog
will appear only if the debugger is NOT attached.
You can always clear the console window by calling:
:lua require("xcodebuild.integrations.dap").clear_console()
Using xcodebuild.nvim you can also check the code coverage after running tests.
- Make sure that you enabled code coverage for desired targets in your test plan.
- Enable code coverage in xcodebuild config:
code_coverage = {
enabled = true,
}
- Toggle code coverage
:XcodebuildToggleCodeCoverage
or:lua require("xcodebuild.actions").toggle_code_coverage(true)
. - Run tests - once they're finished, code coverage should appear on the sidebar with line numbers.
- You can jump between code coverage marks using
:XcodebuildJumpToPrevCoverage
and:XcodebuildJumpToNextCoverage
. - You can also check out the report using
:XcodebuildShowCodeCoverageReport
command.
The plugin sends XcodebuildCoverageToggled
event that you can use to disable other plugins presenting
lines on the side bar (like gitsigns
). Example:
vim.api.nvim_create_autocmd("User", {
pattern = "XcodebuildCoverageToggled",
callback = function(event)
local isOn = event.data
require("gitsigns").toggle_signs(not isOn)
end,
})
Coverage Report Keys:
Key | Description |
---|---|
enter or tab
|
Expand or collapse the current node |
o |
Open source file |
This plugin offers a nice list of failing snapshot tests. For each test it generates a preview image combining reference, failure, and difference images into one. It works with swift-snapshot-testing library.
Run :XcodebuildFailingSnapshots
to see the list.