おはこんばんにちは。ウホーイ(@the_uhooi)です。
本記事では、完璧で究極のエディターである「Neovim」でiOSアプリ開発に挑戦していることを紹介します。
iOSアプリは「Xcode」というIDEを使って開発するのが一般的です。Xcodeを使わずにNeovimでどこまでできるのか、ぜひ最後まで読んでみてください。
余談
最初は真面目に草案を執筆していたのですが、Findyの担当者に見せたら「暴走のウホーレンくらいふざけていいです」と言われたので、思う存分ふざけることにしました。
念のためエビデンスを添付します。

「暴走のウホーレン」とは、私が「iOSDC Japan 2024」というカンファレンスで登壇するトークのタイトルです。
day2 8/24(土) 17:00 Track Aで5分間話すので、ぜひみなさん聴きに来てください。
チケットの購入もまだ間に合います。
トークの宣伝もしていいと担当者に言われているので、エビデンスを(ry

ちなみに本記事のタイトルはiOSDC Japan 2023で非採択となったプロポーザルから取っています。
せっかく考えたので、こちらで使わせてもらいました。
NeovimでSwiftを書くきっかけ
ここから本題です。
まずは私がNeovimでSwiftを書くようになったきっかけを紹介します。
私が一番好きな言語はSwiftです。そして一番好きなエディタはNeovimです。しかし普段はXcodeでSwiftを書いています。Xcodeも好きだし今でもメインで使っています。でもどうせなら一番好きな言語を一番好きなエディタで書きたいと考えるようになりました。これは自然の摂理です。
Neovimのインストール
ここから先は実際に手元でNeovimを動かせたほうが面白いので、インストールします。
基本的にはターミナルで brew install neovim
を実行するのみです。
nvim
を実行して以下の画面が表示されたら成功です。

※iOSアプリ開発ではmacOSが必須なので、本記事はmacOSの利用を前提としています。
NeovimでSwiftを書くための設定
次にNeovimでSwiftを書くための設定です。
ちょうど先日AppleがSwift向けの設定方法を公開したので、こちらに記載されていることはほぼ省略します。
VimやNeovimは設定が大変(それが面白いのですが)なので、気軽に試す場合はLazyVimがオススメです。
自分で設定する人向けに、設定をキーワードベースで紹介します。
- プラグインマネージャー: lazy.nvim
- LSP設定: nvim-lspconfig
- 言語サーバー: SourceKit-LSP(Appleが提供している言語サーバー)
- 補完: nvim-cmp
私の設定も置いておきます。日々更新しているので、よかったらたまに覗いて参考にしてください。
ここまで設定できると、以下ができるようになります。
- シンタックスハイライト
- 補完(iOS SDKを除く)

私はゴリゴリにカスタマイズしているので表示がリッチですが、みなさんの環境でもコードにシンタックスハイライトが付くようになったはずです。
NeovimでiOSアプリ開発するための設定
NeovimでSwiftを書けるようになりましたが、これだけではiOSアプリ開発はできません。
iOS SDK(UIKitなど)を補完できるようにしたり、シミュレータを起動してデバッグできるようにしたりしないとXcodeの代わりにはなり得ません。
これらについて触れられている日本語の記事は見たことないため、詳細に説明します。
iOS SDKを補完できるようにする
SDKのパスを取得し、それをSourceKit-LSP(言語サーバー)へ伝えるよう設定すれば補完できるようになります。
ターミナルで以下のコマンドを実行します。
$ xcrun --sdk iphonesimulator --show-sdk-path /Applications/Xcode-15.4.0.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator17.5.sdk
このパスをSourceKit-LSPへ伝えるよう設定します。
lspconfig.sourcekit.setup { -- … cmd = { 'sourcekit-lsp', '-Xswiftc', '-sdk', '-Xswiftc', '/Applications/Xcode-15.4.0.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator17.5.sdk', '-Xswiftc', '-target', '-Xswiftc', 'x86_64-apple-ios17.5-simulator', }, }
これでUIKitなどが補完できるようになりました。

こちらの設定は以下の記事を参考にさせていただきました。
ブレークポイントを貼ってデバッグできるようにする
Neovimでここまでやるのは難しいと思っていましたが、プラグインを開発してくださった方がいて、そのおかげで私でもできるようになりました。
詳細な設定方法はプラグイン開発者のブログをご参照ください。
仕組みを簡単に説明します。
シンタックスハイライトや補完は「LSP(Language Server Protocol)」というMicrosoftが主にVSCodeのために提供しているプロトコルをNeovimでも使うことで実現しています。
それと同じようにMicrosoftが「DAP(Debug Adapter Protocol)」というプロトコルを提供しており、これを使うことでデバッグを実現します。
例によって設定をキーワードベースで紹介します。
- DAPクライアント: nvim-dap + nvim-dap-ui
- デバッガ: CodeLLDB(LLDB拡張)
- iOSアプリ開発支援: xcodebuild.nvim
予め設定しているNeovimがあるので、どのようにデバッグできるか見てみましょう。
ブレークポイントを貼ります。86と87行目に貼りました。

これでデバッグを実行します。

ビルドに成功するとシミュレータにアプリが表示され、Neovim上でDAPクライアントが起動します。

先ほどブレークポイントを貼った箇所は、画面左下にある「新規」ボタンのタップ時に実行されるので、早速タップしてみましょう。
無事にブレークポイントに引っ掛かりました。

uiState.shouldShowInputScreen
の値をホバーで見てみます。87行目はまだ実行されてないので false
ということがわかります。

ステップオーバーを2回実行して再度値を確認すると、 true
に更新されていることがわかります。

このように、Xcodeを使わなくてもブレークポイントを貼ってデバッグできるようになりました。
右下にある赤い四角のアイコンをタップし、デバッグを終了します。
NeovimとXcodeの比較
NeovimでiOSアプリ開発が一通りできるようになったので、Xcodeと比べてみます。
Neovimでしかできないこと
Neovimは何もできないし、何でもできるともいえます。
デフォルトだと最低限のことしかできません。しかしLuaを使って機能を何でも拡張できるので、ターミナルでできることは理論上すべて実現できるはずです。
Xcodeでできない一例を紹介します。
型情報をインレイヒントで表示する
LSPには「Inlay hint(インレイヒント)」という機能があり、型情報などをインラインに表示できます。
Neovimもプラグインを使うことでインレイヒントを表示できます。
以下の11行目と12行目は型を省略しているのですが、右辺から型推論してそれをインレイヒントとして表示しています。

他にもenumのAssociated valueやforループしている値の型などを表示できます。
このように、Xcodeでは実装されていない機能をカスタマイズして実現できるのがNeovimの魅力のひとつです。
起動が速い
大量のプラグインを起動時に読み込ませている場合を除き、爆速で起動します。
起動が遅い場合も原因を追求してチューニングできます。
Xcodeでしかできないこと
ぶっちゃけ数え切れないほどたくさんあります。
まずターミナルでなくリッチなmacOSアプリであり、Appleが開発しているのでUI/UXも優れています。
補完が強力
おそらくLSPのプロトコルを越えて補完機能を提供しており、非常に強力です。
定義したばかりの変数も補完に表示されます。これは本ビルドではない小さいビルドが常に細かく走っていると思われます。

ログがリッチ
Xcode 15からですが、ログが非常にリッチです。レベルごとに色分けされ、出力元のコードまで一瞬でジャンプできます。

ここまで書いて、頑張ればNeovimでもできそうだと思いました。 私にサッとプラグインを作る力があれば作っていたところでした。
ちなみにiOSアプリ上でもログを表示していますが、以下のライブラリを使えばたったの1行で実装できます。
スターお待ちしています。
おわりに
Neovimはカスタマイズ性に優れ、自分好みのUIにできるところが気に入っています。Luaで気持ちよくカスタマイズできるのも楽しいです。Neovimの設定を書くためにNeovimを使っているといっても過言ではありません。
Neovim×iOSアプリ開発はまだ行っている人が少なく、マジで日本に私しかいないんじゃないかレベルでいないです。そういう私もまだゴリゴリNeovimでSwiftを書いていないのが正直なところです。世界を見渡してもかなり少ないです。
つまり未開拓の領域です。そう聞くとワクワクしませんか?
これを機に一緒に沼へハマりましょう!