程式筆記
Swift

「孤立」させない設計:Swift の依存関係から紐解くアーキテクチャの正解

LANG:
K

kidneyweakx

December 20, 2025 (Updated: December 21, 2025)

Swift の世界では、私たちは強力な Xcode と簡潔な構文に慣れています。しかし、プロジェクトの規模が成長するにつれ、一つの見えない怪物がコードの中に潜み始めます。それは、制御不能な依存関係です。

最近、複数の iOS アプリを開発していますが、プロジェクトが小さいうちは気になりませんでした。しかし、現在のプロジェクト規模は数百ファイルにまで成長しており、今こそ手を打つべき時です。さもなければ、開発効率はいつか崩壊してしまいます。

開発効率の問題は、主に「このファイルを修正したいときに、どの Swift ファイルに影響が出るのかが分からない」ことに起因します。修正している最中にコンパイルが通らなくなることも多いため、静的解析の手助けが欲しいと感じるようになりました。

# なぜ Swift の依存関係はこれほど「混沌」としているのか?

Java、Python、Rust とは異なり、Swift の設計哲学では、同一モジュール内のすべてのファイルがデフォルトで名前空間を共有します。

  1. 消えた import

ほとんどの言語では、別のファイルのクラスを使用する場合、明示的に import する必要があります。しかし、Swift の同一ターゲット内では、そのターゲットに属する internal シンボルに import なしでアクセスできます。この便利さは諸刃の剣です。

  • 利点:開発が速く、冗長なコードが減る。

  • 欠点:ファイル間の依存関係がスパゲッティ状態(Spaghetti code)になる。コンパイルすることなく、User.swift の変更が他のどの Swift ファイルに影響するかを一目で判断するのは非常に困難です。

  1. スクリプト化と xcrun のブラックボックス

Xcode のビルドプロセスは、高度にカプセル化されたブラックボックスです。xcrun を通じて swiftc を呼び出す際、コンパイラはディレクトリ全体をスキャンしてシンボルテーブルを構築します。これは最終製品にとっては有益ですが、開発者が「アーキテクチャの監査」を行うには極めて不親切です。

  • コンパイル時間の激増:依存関係が不明確なため、インクリメンタルビルド(Incremental Build)が頻繁に失敗します。

  • スクリプト化の困難:循環依存をチェックするスクリプトを書こうとしても、Swift には「コンパイルせずに依存関係だけを確認する」ための軽量なツールが根本的に不足しています。


# 構造を可視化する

最近、私は `swift-deps-map` ↗ というツールを開発しました。この記事では、なぜこの取り組みが重要だと考えているのか、そして「軽量な可視化」から始めて、どのように「厳密な静的解析」へと発展させていく計画なのかをお話しします。

なぜこのツールを作ったのか? 設計の初衷

既存のツールがあるならそれを使えばいい、なぜ車輪を再発明するのか。現在市場にある最も正確な依存関係解析ツール(IndexStoreDB https://github.com/swiftlang/indexstore-db ↗ などに基づくもの)には、共通の前提条件があります。それは、「プロジェクトが正常にビルドできること」です。しかし実際の開発において、依存図が最も必要になるのは、コードが「まだ動かない」ときや「構造を調整している」ときです。私が解決したかったのは、そのコンパイル前の空白期間です。

コードが「まだ動かない」ときや「構造を調整している」ときこそ、依存図が必要になります。私はそのコンパイル前の空白期間を解決したいのです。

# 現在の強み:コンパイル前の可視化 (Pre-compilation)

swift-deps-map の現在の売りは「速さ」と「直感性」です。

  • ゼロハードルのスキャン:Xcode 環境やビルド生成物に依存せず、ソースコードだけでスキャン可能です。

  • 視覚的なフィードバック:Mermaid および Cytoscape (JSON) 形式での出力をサポート。あるべきではない矢印を一目で確認できると、リファクタリングへの意欲が大きく高まります。

  • 軽量なゲートキーパー:Pre-commit Hook や CI の初期段階に適しています。コストの高いビルドプロセスが始まる前に、最初の「健康診断」を行うことができます。

# 厳密さへの道:ツールの未来

現在の swift-deps-map は効率的な静的スキャンに頼っていますが、ツールの汎用性を強化するためにいくつかの機能を導入する予定です。

  1. Lint ルールの導入 (Strict Mode)

現在は事実を「表示」するだけですが、今後は Lint ルールを追加する計画です。

  • 循環依存の検出 (Cycle Detection):A が B に依存し、B が A に依存している場合、ターミナルに赤い警告を表示します。

  • 深度の制限:一つのファイルの依存深度が設定値を超えた場合にヒントを表示します。

  1. SwiftSyntax の導入 (意味解析)

正規表現による限界(コメントや文字列の誤判定など)を克服するため、SwiftSyntax の導入を計画しています。これも引き続き Pre-compilation ですが、Swift の抽象構文木 (AST) を正確に理解することで、実際のクラスのインスタンス化なのか、単なる Protocol の宣言なのかを区別できるようになります。

  1. Indexing 統合 (究極の厳密性)

最後に、IndexStoreDB のオプションサポートを追加する計画です。プロジェクトのビルド完了後、ツールは Xcode が生成したインデックスデータベースを読み取ることができます。

  • 100% の正確性:コンパイラの実際のデータを組み合わせます。

  • モジュール間分析:ターゲット内部だけでなく、サードパーティライブラリとの深い関係性も可視化します。

# アーキテクチャを可視化してこそ、コードは美しくなる

多くの場合、コードが乱雑なのは技術不足(あるいは AI の使い方が下手なだけかもしれません)ではなく、見えないコードの依存関係が原因です。

Swift 開発者がシンプルな Python スクリプトで実行でき、Cytoscape のような管理ツールによって「このファイルは関わりすぎていないか(管理範囲が広すぎないか)」を警告してくれる環境を作りたいと考えています。

Shell
uvx --from . swift-deps-map --root . --graph-format cytoscape --graph-output deps.cyto.json

現在、PyPI で公開されています:https://pypi.org/project/swift-deps-map/ ↗

コードもオープンソースです:https://github.com/kidneyweakx/swift-deps-map ↗

ぜひ触ってみて、試して、Issue を立てたり、Swift の依存関係をよりエレガントにする方法について議論しましょう。