No File is an Island: Unraveling Swift Dependencies and Architecture
kidneyweakx
December 20, 2025 (Updated: December 21, 2025)
In the world of Swift, we are accustomed to the power of Xcode and clean syntax. However, as a project's scale grows, an invisible monster begins to haunt the codebase: runaway dependencies.
Lately, I've been developing several iOS apps. When a project is small, it’s hardly noticeable, but as my current projects have grown to hundreds of files, it’s time to take action—otherwise, development efficiency is often ruined.
The core problem with development efficiency is not knowing which Swift files will be affected when you want to modify a specific file, only to find that it fails to compile mid-edit. This is why I found myself wishing for a helpful static analysis tool.
# Why are Swift dependencies so "messy"?
Unlike Java, Python, or Rust, Swift’s design philosophy allows all files within the same Module to share a namespace by default.
1. The Disappearing import
In most languages, if you want to use a class from another file, you must explicitly import it. However, under the same Target in Swift, you don't need to import anything to access internal symbols belonging to that same Target. This convenience is a double-edged sword:
Pro: Fast development, reduced redundant code.
Con: Dependencies between files become a tangled mess (Spaghetti code). It is very difficult to see at a glance which other Swift files will be affected by changing
User.swiftwithout compiling.
2. Scripting and the xcrun Black Box
Xcode’s compilation process is a highly encapsulated black box. When calling swiftc via xcrun, the compiler scans the entire directory to build the symbol table. While this is beneficial for the final product, it is extremely unfriendly for developers performing an "architectural audit":
Souring Compilation Times: Because dependencies are unclear, Incremental Builds often fail.
Difficulty in Scripting: If you want to write a script to check for circular dependencies, you'll find that Swift provides no lightweight tools to "look only at relationships, not compilation."
# Making the Structure Visible
I recently developed `swift-deps-map` . In this article, I want to talk about why I think this is important, and how I am starting with "lightweight visualization" while planning a path toward "rigorous static analysis" in the future.
Why write this tool? The Design Philosophy
Why reinvent the wheel if you can use someone else's? Most of the highly accurate dependency analysis tools currently on the market (such as those based on IndexStoreDB https://github.com/swiftlang/indexstore-db ) share a common prerequisite: the project must compile successfully. However, in actual development, we often need to see the dependency graph most when the code "won't run yet" or is "undergoing structural adjustment." I wanted to solve that pre-compilation gap.
We often need to see the dependency graph most when the code "won't run yet" or is "undergoing structural adjustment." I wanted to solve that pre-compilation gap.
# Current Advantage: Pre-compilation Visualization
The selling point of swift-deps-map currently lies in being "fast" and "intuitive":
Zero-threshold Scanning: It does not depend on the Xcode environment or build artifacts; it only needs the source code to scan.
Visual Feedback: Provides Mermaid and Cytoscape (JSON) exports. When you can see that one arrow that shouldn't be there at a glance, the motivation to refactor increases significantly.
Lightweight Gatekeeper: It is suitable for Pre-commit Hooks or the early stages of CI, performing a first wave "health check" before the expensive Build process begins.
# Towards Rigor: The Future of the Tool
Currently, swift-deps-map relies on high-efficiency static scanning, but I plan to introduce several features to enhance its versatility:
1. Introducing Lint Rules (Strict Mode)
Right now, the tool only "presents" the facts. In the future, I plan to add Lint rules.
Cycle Detection: When A depends on B and B depends on A, it will spit out a red warning directly in the terminal.
Depth Limits: Provide prompts when the dependency depth of a file exceeds a set value.
2. Introducing SwiftSyntax (Semantic Parsing)
To move beyond the limitations of Regex (such as misidentifying comments or strings), I plan to introduce SwiftSyntax. This is still Pre-compilation, but it allows for an accurate understanding of the Swift Abstract Syntax Tree (AST), distinguishing between actual class instantiations and mere Protocol declarations.
3. Indexing Integration (Extreme Rigor)
Finally, I plan to add optional support for IndexStoreDB. Once the project is compiled, the tool can read the index database generated by Xcode.
100% Accuracy: Combined with real data from the compiler.
Cross-Module Analysis: Looking not just inside the Target, but also at deep relationships with third-party libraries.
# Visible Architecture Leads to Beautiful Code
Oftentimes, messy code isn't a result of poor technical skill (maybe it's poor AI skill), but rather invisible code dependencies.
I hope to allow Swift developers to run a simple Python script and have a manager like Cytoscape remind you if a file is "living by the sea" (meddling in too many things).
uvx --from . swift-deps-map --root . --graph-format cytoscape --graph-output deps.cyto.json
It is currently published on PyPI: https://pypi.org/project/swift-deps-map/
The code is also open source: https://github.com/kidneyweakx/swift-deps-map
Feel free to play with it, try it out, raise Issues, or discuss with me how to make Swift dependencies more elegant.
Related Articles
Saved by the Logs: How to Recover a Forgotten Keystore Password via IDE Build Logs.
> 最遙遠的距離,是密碼就在 IDE 裡但我看不見
Hexo 升級 V5 排除疑難雜症
> 因為剛好把電腦重灌,然後這個 blog 就躺在D槽等著我幫他換新的環境,然後他就被我快樂的升級了。 hexo 更新 v5.0 hexo 更新到 V5 的步驟相當容易 就可以自動更新了,但是版本大更新,往往都會附帶一些小問題,像他就給我了幾個warning 更新後 Error 解決方法 修改\ config.yml的部分成新版的,原始external\ link只有true的選項,更新後可決定是否開...
C# 用 VID 和 PID 自動連線 serialport
> 原本 C 的 serial port function官方範例,是類似這樣的 這種方法每次開啟都還要再去跟改port相當的麻煩,所以就找了別人針對windows寫的function,再返回靜態的 port string 給 ,效果相當好。 完整內容可參照下方連結😁 code來源