wojciech-kulik / xcodebuild.nvim

Neovim plugin to Build, Debug, and Test applications created for Apple devices (iOS, macOS, watchOS, etc.)
MIT License
606 stars 14 forks source link
apple ios ios-swift macos neovim nvim swift tvos visionos watchos xcode xcodebuild

๐Ÿ› ๏ธ xcodebuild.nvim

A plugin designed to let you migrate your app development from Xcode to Neovim. It provides all essential actions for development including building, debugging, and testing.

xcodebuild.nvim - unit tests

 

xcodebuild.nvim - debugging

 

โœจ Features

 

๐ŸŒณ File Tree Integration

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.

https://github.com/wojciech-kulik/xcodebuild.nvim/assets/3128467/ed7d2d2e-eaa4-44ea-a1e6-0027ace4fb97

 

โšก๏ธ Requirements

Neovim environment
External tools
Availability of features
Device (iOS <17) Device (iOS 17+) via Network (<17 / 17+) Simulator MacOS
build ๐Ÿ› ๏ธ โœ… โŒ / โœ… โœ… โœ…
(un)install ๐Ÿ› ๏ธ โœ… ๐Ÿ› ๏ธ / โœ… โœ… โŒ
launch ๐Ÿ› ๏ธ โœ… ๐Ÿ› ๏ธ / โœ… โœ… โœ…
run tests ๐Ÿ› ๏ธ โœ… โŒ / โœ… โœ… โœ…
debug ๐Ÿ› ๏ธ ๐Ÿ” ๐Ÿ› ๏ธ โŒ โœ… โœ…
debug tests โŒ โŒ โŒ โœ… โœ…
app logs ๐Ÿชฒ ๐Ÿชฒ โŒ โœ… ๐Ÿชฒ

๐Ÿ” - requires passwordless sudo permission for tools/remote_debugger script (see below).

๐Ÿ› ๏ธ - available if pymobiledevice3 is installed.

๐Ÿชฒ - available while debugging.

 

๐Ÿ“ฆ Installation

Install the plugin using your preferred package manager.

๐Ÿ’ค lazy.nvim

return {
  "wojciech-kulik/xcodebuild.nvim",
  dependencies = {
    "nvim-telescope/telescope.nvim",
    "MunifTanjim/nui.nvim",
    "nvim-tree/nvim-tree.lua", -- (optional) to manage project files
    "stevearc/oil.nvim", -- (optional) to manage project files
    "nvim-treesitter/nvim-treesitter", -- (optional) for Quick tests support (required Swift parser)
  },
  config = function()
    require("xcodebuild").setup({
        -- put some options here or leave it empty to use default settings
    })
  end,
}

Install external tools:

brew install xcode-build-server
brew install xcbeautify
brew install ruby
brew install pipx
gem install xcodeproj
pipx install pymobiledevice3

To quickly install all required tools you can run:

cd ~/.local/share/nvim/lazy/xcodebuild.nvim
make install

[!TIP] Make sure to check out Tips & Tricks!

You will find there a collection of useful tips & tricks for development in Neovim.

 

๐Ÿ“ฑ Setup Neovim For iOS Development

I wrote an article that gathers all the steps required to set up Neovim from scratch to develop iOS and macOS apps:

The Complete Guide To iOS & macOS Development In Neovim

You can also check out the sample Neovim configuration that I prepared for iOS development: ios-dev-starter-nvim

 

๐Ÿš€ Usage

Make sure to open your project's root directory and run XcodebuildSetup to configure it. The plugin needs several information like project file, scheme, config, device, and test plan to be able to run commands.

Remember, that all the information are available in the help :h xcodebuild. Every function is documented and described both in the help and in the code.

๐Ÿ”ง Commands

๐Ÿ‘‰ See all user commands Xcodebuild.nvim comes with the following commands: ### General | Command | Description | | ---------------------------- | -------------------------------------------------------- | | `XcodebuildSetup` | Run configuration wizard to select project configuration | | `XcodebuildPicker` | Show picker with all available actions | | `XcodebuildBuild` | Build project | | `XcodebuildCleanBuild` | Build project (clean build) | | `XcodebuildBuildRun` | Build & run app | | `XcodebuildBuildForTesting` | Build for testing | | `XcodebuildRun` | Run app without building | | `XcodebuildCancel` | Cancel currently running action | | `XcodebuildCleanDerivedData` | Deletes project's DerivedData | | `XcodebuildToggleLogs` | Toggle logs panel | | `XcodebuildOpenLogs` | Open logs panel | | `XcodebuildCloseLogs` | Close logs panel | | `XcodebuildOpenInXcode` | Open project in Xcode | | `XcodebuildQuickfixLine` | Try fixing issues in the current line | | `XcodebuildCodeActions` | Show code actions for the current line | ### Project Manager | Command | Description | | ------------------------------------ | ---------------------------------------------------------- | | `XcodebuildProjectManager` | Show picker with all Project Manager actions | | `XcodebuildCreateNewFile` | Create a new file and add it to target(s) | | `XcodebuildAddCurrentFile` | Add the active file to target(s) | | `XcodebuildRenameCurrentFile` | Rename the current file | | `XcodebuildDeleteCurrentFile` | Delete the current file | | `XcodebuildCreateNewGroup` | Create a new directory and add it to the project | | `XcodebuildAddCurrentGroup` | Add the parent directory of the active file to the project | | `XcodebuildRenameCurrentGroup` | Rename the current directory | | `XcodebuildDeleteCurrentGroup` | Delete the current directory including all files inside | | `XcodebuildUpdateCurrentFileTargets` | Update target membership of the active file | | `XcodebuildShowCurrentFileTargets` | Show target membership of the active file | ๐Ÿ‘‰ To add a file to multiple targets use multi-select feature (by default `tab`). ### Testing | Command | Description | | ---------------------------- | ----------------------------------------- | | `XcodebuildTest` | Run tests (whole test plan) | | `XcodebuildTestTarget` | Run test target (where the cursor is) | | `XcodebuildTestClass` | Run test class (where the cursor is) | | `XcodebuildTestNearest` | Run test (where the cursor is) | | `XcodebuildTestSelected` | Run selected tests (using visual mode) | | `XcodebuildTestFailing` | Rerun previously failed tests | | `XcodebuildTestRepeat` | Repeat the last test run | | `XcodebuildFailingSnapshots` | Show a picker with failing snapshot tests | ### Code Coverage | Command | Description | | ---------------------------------- | ------------------------------------------ | | `XcodebuildToggleCodeCoverage` | Toggle code coverage marks on the side bar | | `XcodebuildShowCodeCoverageReport` | Open HTML code coverage report | | `XcodebuildJumpToNextCoverage` | Jump to next code coverage mark | | `XcodebuildJumpToPrevCoverage` | Jump to previous code coverage mark | ### Test Explorer | Command | Description | | ---------------------------------------- | ------------------------------ | | `XcodebuildTestExplorerShow` | Show Test Explorer | | `XcodebuildTestExplorerHide` | Hide Test Explorer | | `XcodebuildTestExplorerToggle` | Toggle Test Explorer | | `XcodebuildTestExplorerRunSelectedTests` | Run Selected Tests | | `XcodebuildTestExplorerRerunTests` | Re-run recently selected tests | ### Configuration | Command | Description | | -------------------------- | ----------------------------------- | | `XcodebuildSelectScheme` | Show scheme picker | | `XcodebuildSelectDevice` | Show device picker | | `XcodebuildSelectTestPlan` | Show test plan picker | | `XcodebuildShowConfig` | Print current project configuration | | `XcodebuildBootSimulator` | Boot selected simulator | | `XcodebuildInstallApp` | Install application | | `XcodebuildUninstallApp` | Uninstall application |

โŒ˜ Sample Key Bindings

vim.keymap.set("n", "<leader>X", "<cmd>XcodebuildPicker<cr>", { desc = "Show Xcodebuild Actions" })
vim.keymap.set("n", "<leader>xf", "<cmd>XcodebuildProjectManager<cr>", { desc = "Show Project Manager Actions" })

vim.keymap.set("n", "<leader>xb", "<cmd>XcodebuildBuild<cr>", { desc = "Build Project" })
vim.keymap.set("n", "<leader>xB", "<cmd>XcodebuildBuildForTesting<cr>", { desc = "Build For Testing" })
vim.keymap.set("n", "<leader>xr", "<cmd>XcodebuildBuildRun<cr>", { desc = "Build & Run Project" })

vim.keymap.set("n", "<leader>xt", "<cmd>XcodebuildTest<cr>", { desc = "Run Tests" })
vim.keymap.set("v", "<leader>xt", "<cmd>XcodebuildTestSelected<cr>", { desc = "Run Selected Tests" })
vim.keymap.set("n", "<leader>xT", "<cmd>XcodebuildTestClass<cr>", { desc = "Run Current Test Class" })
vim.keymap.set("n", "<leader>x.", "<cmd>XcodebuildTestRepeat<cr>", { desc = "Repeat Last Test Run" })

vim.keymap.set("n", "<leader>xl", "<cmd>XcodebuildToggleLogs<cr>", { desc = "Toggle Xcodebuild Logs" })
vim.keymap.set("n", "<leader>xc", "<cmd>XcodebuildToggleCodeCoverage<cr>", { desc = "Toggle Code Coverage" })
vim.keymap.set("n", "<leader>xC", "<cmd>XcodebuildShowCodeCoverageReport<cr>", { desc = "Show Code Coverage Report" })
vim.keymap.set("n", "<leader>xe", "<cmd>XcodebuildTestExplorerToggle<cr>", { desc = "Toggle Test Explorer" })
vim.keymap.set("n", "<leader>xs", "<cmd>XcodebuildFailingSnapshots<cr>", { desc = "Show Failing Snapshots" })

vim.keymap.set("n", "<leader>xd", "<cmd>XcodebuildSelectDevice<cr>", { desc = "Select Device" })
vim.keymap.set("n", "<leader>xp", "<cmd>XcodebuildSelectTestPlan<cr>", { desc = "Select Test Plan" })
vim.keymap.set("n", "<leader>xq", "<cmd>Telescope quickfix<cr>", { desc = "Show QuickFix List" })

vim.keymap.set("n", "<leader>xx", "<cmd>XcodebuildQuickfixLine<cr>", { desc = "Quickfix Line" })
vim.keymap.set("n", "<leader>xa", "<cmd>XcodebuildCodeActions<cr>", { desc = "Show Code Actions" })

[!TIP] Press <leader>X to access the picker with all commands.

Press <leader>xf to access the picker with all Project Manager commands.

Press <C-r> when the picker with devices is presented to refresh the list.

๐Ÿ“‹ Logs Panel

๐Ÿงช Test Explorer

 

โš™๏ธ Configuration

๐Ÿ”ฅ Customize Xcodebuild.nvim

See default Xcodebuild.nvim config ```lua { restore_on_start = true, -- logs, diagnostics, and marks will be loaded on VimEnter (may affect performance) auto_save = true, -- save all buffers before running build or tests (command: silent wa!) show_build_progress_bar = true, -- shows [ ... ] progress bar during build, based on the last duration prepare_snapshot_test_previews = true, -- prepares a list with failing snapshot tests test_search = { file_matching = "filename_lsp", -- one of: filename, lsp, lsp_filename, filename_lsp. Check out README for details target_matching = true, -- checks if the test file target matches the one from logs. Try disabling it in case of not showing test results lsp_client = "sourcekit", -- name of your LSP for Swift files lsp_timeout = 200, -- LSP timeout in milliseconds }, commands = { cache_devices = true, -- cache recently loaded devices. Restart Neovim to clean cache. extra_build_args = "-parallelizeTargets", -- extra arguments for `xcodebuild build` extra_test_args = "-parallelizeTargets", -- extra arguments for `xcodebuild test` project_search_max_depth = 3, -- maxdepth of xcodeproj/xcworkspace search while using configuration wizard remote_debugger = nil, -- optional path to local copy of remote_debugger (check out README for details) remote_debugger_port = 65123, -- port used by remote debugger (passed to pymobiledevice3) focus_simulator_on_app_launch = true, -- focus simulator window when app is launched }, logs = { -- build & test logs auto_open_on_success_tests = false, -- open logs when tests succeeded auto_open_on_failed_tests = false, -- open logs when tests failed auto_open_on_success_build = false, -- open logs when build succeeded auto_open_on_failed_build = true, -- open logs when build failed auto_close_on_app_launch = false, -- close logs when app is launched auto_close_on_success_build = false, -- close logs when build succeeded (only if auto_open_on_success_build=false) auto_focus = true, -- focus logs buffer when opened filetype = "objc", -- file type set for buffer with logs open_command = "silent botright 20split {path}", -- command used to open logs panel. You must use {path} variable to load the log file logs_formatter = "xcbeautify --disable-colored-output --disable-logging", -- command used to format logs, you can use "" to skip formatting only_summary = false, -- if true logs won't be displayed, just xcodebuild.nvim summary live_logs = true, -- if true logs will be updated in real-time show_warnings = true, -- show warnings in logs summary notify = function(message, severity) -- function to show notifications from this module (like "Build Failed") vim.notify(message, severity) end, notify_progress = function(message) -- function to show live progress (like during tests) vim.cmd("echo '" .. message .. "'") end, }, console_logs = { enabled = true, -- enable console logs in dap-ui format_line = function(line) -- format each line of logs return line end, filter_line = function(line) -- filter each line of logs return true end, }, marks = { show_signs = true, -- show each test result on the side bar success_sign = "โœ”", -- passed test icon failure_sign = "โœ–", -- failed test icon show_test_duration = true, -- show each test duration next to its declaration show_diagnostics = true, -- add test failures to diagnostics }, quickfix = { show_errors_on_quickfixlist = true, -- add build/test errors to quickfix list show_warnings_on_quickfixlist = true, -- add build warnings to quickfix list }, test_explorer = { enabled = true, -- enable Test Explorer auto_open = true, -- open Test Explorer when tests are started auto_focus = true, -- focus Test Explorer when opened open_command = "botright 42vsplit Test Explorer", -- command used to open Test Explorer, must create a buffer with "Test Explorer" name open_expanded = true, -- open Test Explorer with expanded classes success_sign = "โœ”", -- passed test icon failure_sign = "โœ–", -- failed test icon progress_sign = "โ€ฆ", -- progress icon (only used when animate_status=false) disabled_sign = "โธ", -- disabled test icon partial_execution_sign = "โ€", -- icon for a class or target when only some tests were executed not_executed_sign = " ", -- not executed or partially executed test icon show_disabled_tests = false, -- show disabled tests animate_status = true, -- animate status while running tests cursor_follows_tests = true, -- moves cursor to the last test executed }, code_coverage = { enabled = false, -- generate code coverage report and show marks file_pattern = "*.swift", -- coverage will be shown in files matching this pattern -- configuration of line coverage presentation: covered_sign = "", partially_covered_sign = "โ”ƒ", not_covered_sign = "โ”ƒ", not_executable_sign = "", }, code_coverage_report = { warning_coverage_level = 60, error_coverage_level = 30, open_expanded = false, }, integrations = { xcode_build_server = { enabled = false, -- run "xcode-build-server config" when scheme changes }, nvim_tree = { enabled = true, -- enable updating Xcode project files when using nvim-tree guess_target = true, -- guess target for the new file based on the file path should_update_project = function(path) -- path can lead to directory or file -- it could be useful if you mix Xcode project with SPM for example return true end, }, neo_tree = { enabled = true, -- enable updating Xcode project files when using neo-tree.nvim guess_target = true, -- guess target for the new file based on the file path should_update_project = function(path) -- path can lead to directory or file -- it could be useful if you mix Xcode project with SPM for example return true end, }, oil_nvim = { enabled = true, -- enable updating Xcode project files when using oil.nvim guess_target = true, -- guess target for the new file based on the file path should_update_project = function(path) -- path can lead to directory or file -- it could be useful if you mix Xcode project with SPM for example return true end, }, quick = { -- integration with Swift test framework: github.com/Quick/Quick enabled = true, -- enable Quick tests support (requires Swift parser for nvim-treesitter) }, }, highlights = { -- you can override here any highlight group used by this plugin -- simple color: XcodebuildCoverageReportOk = "#00ff00", -- link highlights: XcodebuildCoverageReportOk = "DiagnosticOk", -- full customization: XcodebuildCoverageReportOk = { fg = "#00ff00", bold = true }, }, } ```

๐ŸŽจ Customize Highlights

See all highlights #### Test File | Highlight Group | Description | | ----------------------------------- | ------------------------------ | | `XcodebuildTestSuccessSign` | Test passed sign | | `XcodebuildTestFailureSign` | Test failed sign | | `XcodebuildTestSuccessDurationSign` | Test duration of a passed test | | `XcodebuildTestFailureDurationSign` | Test duration of a failed test | #### Test Explorer | Highlight Group | Description | | -------------------------------------------- | --------------------------- | | `XcodebuildTestExplorerTest` | Test name (function) | | `XcodebuildTestExplorerClass` | Test class | | `XcodebuildTestExplorerTarget` | Test target | | `XcodebuildTestExplorerTestInProgress` | Test in progress sign | | `XcodebuildTestExplorerTestPassed` | Test passed sign | | `XcodebuildTestExplorerTestFailed` | Test failed sign | | `XcodebuildTestExplorerTestDisabled` | Test disabled sign | | `XcodebuildTestExplorerTestNotExecuted` | Test not executed sign | | `XcodebuildTestExplorerTestPartialExecution` | Not all tests executed sign | #### Code Coverage (inline) | Highlight Group | Description | | --------------------------------------- | ------------------------------------ | | `XcodebuildCoverageFullSign` | Covered line - sign | | `XcodebuildCoverageFullNumber` | Covered line - line number | | `XcodebuildCoverageFullLine` | Covered line - code | | `XcodebuildCoveragePartialSign` | Partially covered line - sign | | `XcodebuildCoveragePartialNumber` | Partially covered line - line number | | `XcodebuildCoveragePartialLine` | Partially covered line - code | | `XcodebuildCoverageNoneSign` | Not covered line - sign | | `XcodebuildCoverageNoneNumber` | Not covered line - line number | | `XcodebuildCoverageNoneLine` | Not covered line - code | | `XcodebuildCoverageNotExecutableSign` | Not executable line - sign | | `XcodebuildCoverageNotExecutableNumber` | Not executable line - line number | | `XcodebuildCoverageNotExecutableLine` | Not executable line - code | #### Code Coverage (report) | Highlight Group | Description | | --------------------------------- | ---------------------------------------------------- | | `XcodebuildCoverageReportOk` | Percentage color when above `warning_coverage_level` | | `XcodebuildCoverageReportWarning` | Percentage color when below `warning_coverage_level` | | `XcodebuildCoverageReportError` | Percentage color when below `error_coverage_level` |

๐Ÿค– Customize Behaviors

See all auto commands You can customize integration with xcodebuild.nvim plugin by subscribing to notifications. Example: ```lua vim.api.nvim_create_autocmd("User", { pattern = "XcodebuildTestsFinished", callback = function(event) print("Tests finished (passed: " .. event.data.passedCount .. ", failed: " .. event.data.failedCount .. ")" ) end, }) ``` Use `print(vim.inspect(event.data))` to see what is exactly provided in the payload. Below you can find a list of all available auto commands. | Pattern | Provided Data (`event.data`) | | ---------------------------------- | ---------------------------------------------------------------------------------- | | `XcodebuildBuildStarted` | `forTesting (Bool)` | | `XcodebuildBuildStatus` | `forTesting (Bool), progress (Int? [0-100]), duration (Int)` | | `XcodebuildBuildFinished` | `forTesting (Bool), success (Bool), cancelled (Bool), errors (ParsedBuildError[])` | | `XcodebuildTestsStarted` | none | | `XcodebuildTestsStatus` | `passedCount (Int), failedCount (Int)` | | `XcodebuildTestsFinished` | `passedCount (Int), failedCount (Int), cancelled (Bool)` | | `XcodebuildApplicationLaunched` | none | | `XcodebuildActionCancelled` | none | | `XcodebuildProjectSettingsUpdated` | `(ProjectSettings)` | | `XcodebuildTestExplorerToggled` | `visible (Bool), bufnr (Int?), winnr (Int?)` | | `XcodebuildCoverageToggled` | `(Bool)` | | `XcodebuildCoverageReportToggled` | `visible (Bool), bufnr (Int?), winnr (Int?)` | | `XcodebuildLogsToggled` | `visible (Bool), bufnr (Int?), winnr (Int?)` |

๐Ÿ“ฆ Swift Packages Development

This plugin supports only applications. However, if you develop Swift Package for one of supported platforms, you can easily use this plugin by creating a sample iOS/macOS project in your root directory and by adding your package as a dependency.

 

๐Ÿ”ฌ Debugger Configuration

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:

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,
}

 

๐Ÿ“ฒ Debugging On iOS 17+ Device

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. You can even make a local copy if you are worried that the content of this file could be changed in the future.

Passwordless access to remote_debugger

You can disable password requirement by updating /etc/sudoers file. Make sure to use the command below, otherwise you may break your sudo command:

sudo visudo -f /etc/sudoers

Append this line, but first update the path and the username:

YOUR_USERNAME ALL = (ALL) NOPASSWD: /Users/YOUR_USERNAME/.local/share/nvim/lazy/xcodebuild.nvim/tools/remote_debugger

Creating a local copy of remote_debugger

If you don't want to configure the passwordless permission to the file that could be changed in the future, you can make a local copy of this script, set your local path in the config commands.remote_debugger, and update /etc/sudoers accordingly.

Please remember that you will have to update this file manually if it changes in the future.

 

๐Ÿ› Application Logs

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()

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.

 

๐Ÿšฅ Lualine Integration

You can also integrate this plugin with lualine.nvim.

xcodebuild.nvim - lualine
See Lualine configuration ```lua 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]) |

 

๐Ÿงช Code Coverage

xcodebuild.nvim - coverage report
See how to configure Using xcodebuild.nvim you can also check the code coverage after running tests. 1. Make sure that you enabled code coverage for desired targets in your test plan. 2. Enable code coverage in xcodebuild [config](#%EF%B8%8F-configuration): ```lua code_coverage = { enabled = true, } ``` 3. Toggle code coverage `:XcodebuildToggleCodeCoverage` or `:lua require("xcodebuild.actions").toggle_code_coverage(true)`. 4. Run tests - once they're finished, code coverage should appear on the sidebar with line numbers. 5. You can jump between code coverage marks using `:XcodebuildJumpToPrevCoverage` and `:XcodebuildJumpToNextCoverage`. 6. 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: ```lua 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 |

 

๐Ÿ“ธ Snapshot Tests Preview

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.

xcodebuild.nvim - snapshot testing

 

๐Ÿ‘จโ€๐Ÿ’ป API

If you want to use functions directly instead of user commands, then please see xcodebuild.actions module.

 

๐Ÿงฐ Troubleshooting

[!IMPORTANT] The first thing you should do is to check the output of :checkhealth xcodebuild and fix all issues.

Run it from your project root directory.

Configuration

Processing the project configuration is a very complex task that relies on parsing multiple crazy outputs from xcodebuild commands. Those logs are a pure nightmare to work with. This process may not always work.

In case of any issues with, you can try manually providing the configuration file .nvim/xcodebuild/settings.json in your root directory.

See a sample settings.json file ```json { "bundleId": "com.company.bundle-id", "show_coverage": false, "deviceName": "iPhone 15", "destination": "28B52DAA-BC2F-410B-A5BE-F485A3AFB0BC", "config": "Debug", "testPlan": "YourTestPlanName", "projectFile": "/path/to/project/App.xcodeproj", "scheme": "App", "platform": "iOS Simulator", "productName": "App", "projectCommand": "-workspace '/path/to/project/App.xcworkspace'", "xcodeproj": "/path/to/project/App.xcodeproj", "lastBuildTime": 10, "os": "17.2", "appPath": "/Users/YOU/Library/Developer/Xcode/DerivedData/App-abafsafasdfasdf/Build/Products/Debug/App.app" } ``` - `platform` - check out `lua/xcodebuild/core/constants.lua` for available platforms - `destination` - simulator ID - `projectFile` / `projectCommand` - can be `xcodeproj` or `xcworkspace`, the main project file that you use

Tests

If you encounter issues with test detection, you may want to read this: Test File Search - File Matching.

LSP

In most cases, it's enough to run the project in Xcode, clean it CMD+Shift+K, build again CMD+B, and run xcode-build-server config again.

Useful Help Tags