-
-
Notifications
You must be signed in to change notification settings - Fork 24
Tips & Tricks
xcodebuild
CLI tool is slower than Xcode. This section provides a workaround to improve the build time.
The issue is caused by the fact that xcodebuild
tries to connect to the Apple
servers before building the project, which can take 20 seconds or more.
Usually, those requests are not necessary, but they slow down each build.
The workaround blocks developerservices2.apple.com
domain when the xcodebuild
tool is running by
modifying /etc/hosts
. Below you can find three ways to enable the workaround.
Warning
Keep in mind that disabling access to developerservices2.apple.com
for
xcodebuild
may cause some issues with the build process. It will disable
things like registering devices, capabilities, and other network-related
features. Therefore, it's best to use it when you are working just on the
code and don't need updating project settings.
1. Manual (script)
Enable workaround:
sudo bash -c "echo '127.0.0.1 developerservices2.apple.com' >>/etc/hosts"
Disable workaround:
sudo sed -i '' '/developerservices2\.apple\.com/d' /etc/hosts
2. Manual (network sniffer)
If you use some tool to sniff network traffic like Proxyman or Charles Proxy,
you can block requests to https://developerservices2.apple.com/*
and
automatically return some error like 999 status code. It will prevent
xcodebuild
from further calls.
3. Automatic (xcodebuild.nvim
integration)
In this approach the Apple server will be blocked only when the xcodebuild
command (triggered by the plugin) is running. However, it requires a passwordless
sudo
permission for the script.
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 that automatically blocks Apple servers
Update your config with:
integrations = {
xcodebuild_offline = {
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/xcodebuild_offline" && \
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/xcodebuild_offline\" >> /etc/sudoers"
More details about this issue can be found here: #201
If you switch between Neovim and Xcode you most likely want a feature that automatically refreshes changed files.
The only reliable solution I found is:
vim.opt.autoread = true
-- refresh files if changed outside
vim.fn.timer_start(2000, function()
vim.cmd("silent! checktime")
end, { ["repeat"] = -1 })
Often, you want to have templates for specific files in your project or at least you want to have a header (like when using Xcode).
I created a small function that fills in a new file with a predefined template. Based on the file name suffix it can use the relevant template.
This function is also able to replace some placeholders like {date}
, {filename}
, and {name}
. If you place {cursor}
in your template, the cursor will be automatically moved there.
vim.api.nvim_create_autocmd({ "BufNewFile", "BufReadPost" }, {
group = "bufcheck",
pattern = "*.swift",
callback = function(ev)
local lines = #vim.api.nvim_buf_get_lines(ev.buf, 0, -1, false)
if lines > 1 then
return
end
local filename = string.match(ev.file, "([^/]*)%.swift")
local name = filename
-- TODO: make sure this path leads to your folder with templates!!
local basepath = os.getenv("HOME") .. "/.config/YOUR_CONFIG_PATH/templates/"
local templates = { "View", "ViewModel", "Builder", "Router", "Tests", "Spec" }
local template
local cursor
for _, templateSuffix in ipairs(templates) do
if vim.endswith(filename, templateSuffix) then
template = vim.fn.readfile(basepath .. string.lower(templateSuffix) .. ".txt")
name = string.gsub(name, templateSuffix, "")
break
end
end
template = template or vim.fn.readfile(basepath .. "empty.txt")
for i = 1, #template do
template[i] = string.gsub(template[i], "{date}", os.date("%d/%m/%Y"))
template[i] = string.gsub(template[i], "{filename}", filename)
template[i] = string.gsub(template[i], "{name}", name)
if cursor == nil and string.find(template[i], "{cursor}") then
cursor = { i, tonumber(string.find(template[i], "{cursor}")) + 1 }
end
template[i] = string.gsub(template[i], "{cursor}", " ")
end
vim.api.nvim_buf_set_lines(ev.buf, 0, -1, false, template)
if cursor then
vim.api.nvim_win_set_cursor(0, cursor)
end
vim.cmd("w")
end,
})
Sample template templates/viewmodel.txt
//
// {filename}.swift
//
//
// Created by YOUR_NAME on {date}.
// Copyright © 2024 YOUR_COMPANY. All rights reserved.
//
import Foundation
protocol {filename}Protocol: ObservableObject {
var someProperty: String { get }
}
final class {filename}: {filename}Protocol {
@Published var someProperty: String = ""
init() {
{@cursor@}
}
}
If you want to try out new features from sourcekit-lsp, you need to switch to Swift Development snapshot. Below you can find steps required to do that:
- Download the latest snapshot for Xcode from here
- Install it.
- Switch to it from Xcode (menu bar -> Xcode -> Toolchains)
- Run (make sure that the path matches your version)
sudo cp `xcode-select -p`/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/*/lib/darwin/libclang_rt.*.a /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-03-07-a.xctoolchain/usr/lib/clang/17/lib/darwin/
- Switch your LSP config to point to the new
sourcekit-lsp
:lspconfig["sourcekit"].setup({ capabilities = capabilities, on_attach = on_attach, cmd = { "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-03-07-a.xctoolchain/usr/bin/sourcekit-lsp", }, -- ... },
- Add additional flags to build and test from Neovim using the latest toolchain. Update your xcodebuild.nvim setup options:
commands = { extra_build_args = "-parallelizeTargets -toolchain swift-latest", extra_test_args = "-parallelizeTargets -toolchain swift-latest", },
- Clean & build the project from Xcode.
- Open project in Neovim as usual. New features like smart rename should be available.
To see changes in your view without rebuilding the app, use the "hot reload" feature offered by the InjectionIII
app together with the Inject
Swift Package:
Short Installation Guide (SwiftUI)
- Add
Inject
Swift Package to your project. - Add
@ObserveInjection var injection
to your view. - Add
.enableInjection()
modifier at the end of yourbody
. - Add
-Xlinker -interposable
toOther Linker Flags
in your Build Settings. Each flag in a separate line. - Install & run the
InjectionIII
app (download from repo releases). - Click on the icon in the Menu Bar, select
Open Project
, and select your project folder. - Run the app. Now, you should be able to modify your view and changes should be hot reloaded.
For macOS, you may need to temporarily disable sandbox + Disable Library Validation
in Hardened Runtime
section.
There is no easy way to debug view hierarchy outside of Xcode.
However, there is a paid alternative. If you are interested, check out this issue: #213.
The plugin automatically symbolicates the crash call stack visible in the nvim-dap-ui console window. However, symbolication may not be possible in certain scenarios, such as when running a macOS app. In such cases, you can use a solution provided by @FelixLisczyk HERE. This snippet allows you to select the call stack and view the symbolicated results in a new tab.