Neovim and Swift, a Match Made in Heaven

Photo by Justin Greene on Unsplash

Every once in a while two technologies will converge, yielding a sum that is greater than their parts. If you are lucky enough, you will witness just such an event once or maybe twice in your life. Swift LSP combined with Neovim is one of these cataclysmic events. Hyperbole aside, I’m really excited about the prospect of effectively writing Swift outside of XCode. Once the project was announced, I couldn’t wait to dive in and integrate it with my favorite editor.

What is Swift LSP?

Recently the Swift team announced that they would be releasing an LSP server (Language Server Protocol) for Swift. An LSP server, developed for a particular language, allows any editor which implements an LSP client to attain many IDE like features for editing that language, for free. These features include code completion, formatting, refactoring, go to definition, find references, contextual documentation look up and much more.

Before LSP, each IDE or editor had to implement all the above features for each language they supported. As a result, most editors would have a patchwork of functionality implemented for the few languages they supported. Polyglot developers would have to learn a new IDE for each language they used or do without advanced editing features.

While the Swift team has made amazing progress on the LSP server, it is still an immature project. Currently the only supported LSP features are code completion, go to definition, documentation lookup and find all references. Even though this is only a small subset of what LSP has to offer, it is already a big boost to developer productivity.

How it works

Before I get into how to integrate the server with Neovim, I need to talk a bit about how an LSP server works. An LSP server needs to know how all the source files in a project relate to each other. Therefore, the server must know something about how the project is built. It needs to know which files are included in the project, where to find dependencies and which flags are passed to the compiler. The Swift LSP uses the Swift Package Manager to understand how the project is built. As such, if you want to use Swift LSP, your project needs be a Swift package. I wont go into detail on creating a package but you will need a Package.swift at the root of your project.

You will also need to install the Swift tool chain describe on the Swift LSP website. You will then need to define SOURCEKIT_TOOLCHAIN_PATH in your .zshrc. It will look something like export SOURCEKIT_TOOLCHAIN_PATH=<path to toolchain>

Additionally, we will need an LSP Client. In our case, we will use LanguageClient-neovim (LCN form here on out), a Neovim plugin. Not only will this give you IDE like features for Swift but also for any other language implementing an LSP server.

I’ve created a demo repository that handles installing all the required Neovim plugins, downloads and build the LSP server and implements a small Swift package to experiment with. It can be found here. It uses the Dein plugin manager for Neovim to install plugins. It also uses Deoplete for code completion. LCN will fallback to use omniFunc to do completion so Deoplete isn’t strictly required. The project uses its own vimrc and puts all plugins in the cache folder so your Neovim installation should not be affected.

How to use the demo repo

To use the repository, there are 3 steps:

  1. Clone my repo, cd into it and ./init.sh. This will fetch the sourcekit-lsp submodule, build it and build the demo Swift package found at MySwiftPackage. It will also update neovim with the Dein, Deoplete and LCN plugins.
  2. Download and install the Swift toolchain recommended on the sourcekit-lsp website(12-4-2018 at the time of this writing). Make sure SOURCEKIT_TOOLCHAIN_PATH in tryit.sh points to the newly installed xctoochain file.
  3. Run tryit.sh which will set some environment variables and launch Neovim into the main.swift file. At this point you are ready to start experimenting!
  4. In Neovim, run :UpdateRemotePlugins

Things to try

  1. If you move the cursor over a symbol eg. MyStruct and type gd in normal mode. You will be taken to the MyStruct definition in the Greeter.swift file. gd is mapped to :call LanguageClient#textDocument_definition()<CR>
  2. Open a line under line 4 and type my_array.. You should see a list of all methods and properties defined by Array. You should be able to Tab through all the options.
  3. Move the cursor over first in my_array.first! and type :call LanguageClient#textDocument_hover()<CR>. This should open up a preview window with the documentation for Array.first property.
  4. Type :call LanguageClient_contextMenu()<CR>. This will bring up a context menu allowing you to select from the actions above. Most options are not implemented yet but gives you an idea of what will be possible in the future.
  5. The Swift LSP server docs say that find all references is supported but I wasn’t able to make it work. If you figure it out, drop me a line.

As you can see, the functionality is currently somewhat limited but the Swift team is moving fast. Even as I wrote this new commits were being pushed. Neovim is a pleasure to use and with the advent of the Swift LSP server I could see my self moving to it for all my Swift development needs.

Caveats

There are a few catches that I ran into. First of all, you need to tell LCN how to find the root of the Swift project. In the vimrc file I told LCN to look for a file called Package.swift which indicates the root of a Swift project. By default LCN looks for a .git folder which, in our case, is one level up from our SPM root. Swift LSP also doesn’t index in the background, it will only index a project’s symbols at build time. This means that the LSP server wont be aware of any new symbols added to a project until it is recompiled. The Swift LSP project is moving fast so make sure you always have the latest versions of everything.

I'm a freelance iOS developer based in San Francisco. Feel free to contact me.

WWDC Refactored

Recently, I’ve been discussing ways to architect iOS applications to make them easier to test. Yesterday, I stumbled upon this talk from WWDC ‘17. In this video, the presenter espouses a lot of the same ideas I’ve been advocating here. It’s a great video and I highly recommend it.

Pt. 3 Swift Testability By Example

In my last article, I discuss the easiest path to testable Swift. In that article I list qualities that make tests valuable. Additionally, I show that business and application logic should be decoupled from volatile or asynchronous dependencies. Now I’d like to focus on the “State” object that houses all of this logic and illustrate some design decisions that will make it easier to test.
An experienced tester knows that certain features are trivial to test while others must be mangled and distorted before they yield to testing. Tests that are easy to write are ones that require little or no arrangement code, don’t require new classes to enable testing (ie. mocks or stubs) and produce easily verifiable output. The easiest thing in the world to test is a pure function:

Pt. 1 Effective Unit Testing in Swift

I’m coming clean. I’m embarrassed to admit it, but I’ve been a professional software developer for twelve years and have hardly written any tests. I’ve embraced the unit testing cult a few times, but every time I did, I spent more time hacking my code to make it testable than writing new features. My velocity went into the toilet, my once beautiful code became riddled with concessions to make it testable, tests would break after small changes, and worst of all, they never seemed to surface any genuine defects. I would eventually give up with the excuse that “iOS code isn’t testable” or “unit tests are for people who write bad code”. Such hubris!

Obscure Programming Laws And What They Can Do For You

In my last post I explained some of the common anti-patterns that developers fall into. In this article I’d like to explore some architectural principles that help make code more robust and extensible. Like all ‘rules’ in software development, these are really only guidelines, so if something feels awkward or forced, it probably is.

Bringing Your Mobile User Experience To The Next Level

Developers, repeat after me, “My app is not a web page”. Again! “My app is not a wep page”. It seems that most developers are writing their mobile apps like they did web pages in the 1990’s. They makes requests to the server, put the app into a waiting state, and display the response in the UI. If they are offline, an error is reported to the user and the app is unusable. This experience is fine for a webapp in the browser with little or no local storage and a ubiquitous network connection, but falls short in the mobile world.

Keep Your Sanity With Event Handling State Machines

In my previous post I taught you how to wrangle your game initialization code in order to keep it from breaking with every refactor. Today, I’d like to talk about event handling and how to get a handle on it (pun intended). If you’re anything like me, your event handling code (generally your touchesEnded:withEvent:) is the longest method in your whole application; littered with switch statements, if/else clauses and enough boolean flags to make George Boole turn over in his grave.

Sleep Easy with Sequencing and Dependency Resolution

Writing games is hard. Writing games for iOS is even harder. Typically, one of the most fragile and error prone parts of a game is the loading and initialization process. For obvious reasons, it is also one of the most critical pieces of your app. By using a sequencer, developers can define dependencies for steps in their loading process thereby removing some of the brittleness and instability from their game.