Photo by Anton Darius | @theSollers on Unsplash
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:
- Clone my repo, cd into it and
./init.sh. This will fetch the
sourcekit-lspsubmodule, build it and build the demo Swift package found at
MySwiftPackage. It will also update neovim with the Dein, Deoplete and LCN plugins.
- Download and install the Swift toolchain recommended on the sourcekit-lsp website(12-4-2018 at the time of this writing). Make sure
tryit.shpoints to the newly installed xctoochain file.
tryit.shwhich will set some environment variables and launch Neovim into the
main.swiftfile. At this point you are ready to start experimenting!
- In Neovim, run :UpdateRemotePlugins
Things to try
- If you move the cursor over a symbol eg.
gdin normal mode. You will be taken to the
MyStructdefinition in the
gdis mapped to
- 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.
- Move the cursor over
:call LanguageClient#textDocument_hover()<CR>. This should open up a preview window with the documentation for
: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.
- 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.
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.
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.
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:
In my last article I touched on a few ideas to make Swift testable. Here I’d like to demonstrate these ideas by implementing a hypothetical feature. The feature is written using TDD and exhibits these virtuous patterns. Before I go on, I’d like to describe the attributes which make a test good.
Enlightenment is intimacy with all things. —Ehei Dogen (1200-1253)
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!
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.
Being a freelance iOS developer, I get the opportunity to work on a variety of projects, written by developers with varying degrees of experience. This has given me the opportunity to catalog the most common iOS anti patterns or ‘Code smells’ I’ve smelt. I will enumerate the most common smells and some possible solutions to guide you on your path to mobile app nirvana.
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.
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.
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.