MVVMLintの紹介
こんにちは。ソフトウェアエンジニアの@kitasuke です。
今回は、社内プロジェクト向けに作ったSwiftで実装されている静的解析ツールの MVVMLint を紹介します。
概要
データを扱うビジネスロジックに用いるアーキテクチャに対して、正しい方法でコードが書かれているか確認したいという需要がありました。 コンパイラはコードが正しいシンタックスで書かれているかを確認できますが、静的解析ツールを使えばコードが正しい方法で使われているかも確認できます。 MVVMLintは、単一方向なデータフローを実現するMVVMアーキテクチャにおいて、データのやり取りが漏れなく行われているかを検知します。 人間の目で確認することも大事ですがツールでも検知できるとチームの生産性が向上し、より良いプロダクト開発に繋がります。
前置き
MVVMLintは特定のプロジェクト用に実装されているので、他のプロジェクトでは使えない可能性が高いです。 今回は、静的解析ツールのアイデアがあるがどう実装すれば良いか分からない場合に対する具体例としての紹介です。 Swift用のツールなので、Objective-Cで書かれたコードには使えません。
詳細内容
KickstarterのMVVM
KickstartのOSS iOSアプリ を参考にしたアーキテクチャです。 Inputs/OutputsがプロトコルとしてViewModelに定義されていて、それらをViewControllerから使ってデータフローにアクセスします。
protocol FooViewModelInputs {
func viewDidLoad()
func buttonTapped(data: Data)
}
protocol FooViewModelOutputs {
var reloadData: (() -> Void)? { get set }
var showError: (() -> Error)? { get set }
}
また、Inputs/OutputsをプロトコルではなくEnumで定義したパターンも対象にしています。
enum Inputs {
case viewDidLoad
case buttonTapped(Data)
}enum Outputs {
case reloadData
case showError(Error)
}
Enumで定義するとパターンマッチングを活かせるので、コンパイル時に網羅性を保証できるのが良い点です。
SwiftSyntax
このツールは SwiftSyntaxを使用して実装しています。 SwiftSyntax はAppleが公開した libSyntax のSwift API用のツールで、静的解析に必要な情報を提供しています。
SwiftSyntaxで定義されている SyntaxVisitor
を使うと、 ProtocolDeclSyntax
や EnumDeclSyntax
などのシンタックスにアクセスできます。 それらを利用して、 ViewModel
で定義されているプロトコルやEnumの定義を取得して、 ViewController
でそれらが呼ばれているかを検知します。
具体的な使用方法はこちらの本で解説しているので、興味があればご覧ください。
実行結果
ターミナルでこのツールを実行すると、下記のような結果が出力されます。 下記のように呼び忘れのInputs/Outputsがある場合は、該当するファイルを参照して修正すれば良いです。
$ mvvmlint run --paths "~/dev/"
input: viewDidLoad not called at: ~/dev/xxx.swift
output: showHUD not called at: ~/dev/zzz.swift
output: dismissHUD not called at: ~/dev/zzz.swift
まとめ
同じようなことは、 SwiftLint を使って未使用の変数を検知するのでも可能です。 しかし、今回のように特定の用法があるコードをビジネスロジックのみ対象として検知する場合は、それ用に静的解析ツールを作るのは価値があると思います。専用のツールだと、解析したい内容をよりカスタマイズできるので表現力を高く実現できます。