CLI tool
onevcat/FengNiao avatar
onevcat/FengNiao

FengNiao: a Swift command line tool that deletes unused Xcode image resources

A command line tool for cleaning unused resources in Xcode.

3,574 stars248 forksSwiftMIT

At a glance

What is it?
FengNiao scans an Xcode project for resource files that no source file references and offers to delete them. It is a small, single-purpose utility from onevcat, and its risk profile is worth understanding before you run it on a real project.
Who is it for?
FengNiao fits teams with a large, old Xcode project whose asset catalogs have accumulated unused imagesets, jpgs, pngs, gifs and pdfs, and who already keep the project in version control. It is a poor fit for anyone working on a project without a clean git state, and for projects where resources are referenced in ways the scanner does not model, such as server-driven names.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 144 days ago.
What is it written in?
Mainly Swift, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem FengNiao targets: dead images in an Xcode asset catalog

Xcode projects accumulate image files that nobody references anymore. A designer swaps a banner, a feature is removed, an old launch image stays in the catalog. Nothing breaks, because unused assets are never loaded at runtime, so the files sit there for years. The cost is repository size, slower clones, and a growing pile of names that make it harder to tell which asset is live.

FengNiao is a command line util, in the README's words, "to delete unused image resource files from your Xcode project." It is written in Swift and targets macOS and Linux according to its badges. The intended user is an iOS or macOS developer who wants a mechanical answer to the question of which images are dead, rather than clicking through an asset catalog by hand.

The scope is deliberately narrow. It handles resource files, not Swift source, not storyboard structure, not localisation strings. If your problem is unused code, this is the wrong tool, and the README does not pretend otherwise.

How the scan works: names extracted, then regex-matched against source

The mechanism is name-based, and the README lays it out in three steps. First, FengNiao extracts resource file names from folders named imageset, launchimage, appiconset and bundle. The default resource extensions are imageset, jpg, png, gif and pdf. Second, it uses a regular expression to search for all string names in files of type m, mm, swift, xib, storyboard and plist. Third, it subtracts the used names from the resource names, and whatever is left is reported as unused.

That design has a direct consequence: FengNiao does not parse your code. It does not resolve a variable that holds an image name, and it does not know that a name is assembled at runtime. Any asset whose name never appears literally in one of the searched file types will be classified as unused. The README does not document an exception list for such cases, and the --exclude flag excludes paths from the search, not individual resource names.

The default file extension lists are also worth reading carefully. If your project keeps image names in a .json or .strings file, that file type is not in the default search set, so names referenced only there will not be counted as used. The flags -r and -f exist precisely so you can widen or narrow those sets, and changing them changes the result.

Installing FengNiao with Mint or from source

The README gives two installation routes. The first uses Mint, which installs and runs Swift command line tool packages. It requires Xcode to be installed. The commands are brew install mint, then mint install onevcat/fengniao.

bash
brew install mint
mint install onevcat/fengniao

The second route compiles the tool from source with Swift Package Manager, then copies the binary into your PATH. The README uses /usr/local/bin and renames the executable to lowercase fengniao.

bash
git clone https://github.com/onevcat/FengNiao.git
cd FengNiao
swift build -c release
sudo cp .build/release/FengNiao /usr/local/bin/fengniao

After either route, running fengniao with no arguments from your project folder scans the current folder and all subfolders. It then asks whether you want to delete what it found. The README is explicit that this is "an un-restorable operation" and that you should have a backup or a version control system first.

A safer first run is the daily-work form the README shows, which points at the current project and skips third-party folders. Carthage and Pods are the examples given, because those may contain resources you do not want touched.

bash
fengniao --project . --exclude Carthage Pods

If you only want to see the list, --list-only lists unused files and exits without prompting. That is the flag to reach for before you let the tool delete anything.

Wiring FengNiao into an Xcode build phase

The README describes adding a Run Script phase in the Build Phases tab, dragged above Copy Bundle Resources. The suggested script content is fengniao --exclude Carthage --force. In that context there is no opportunity to confirm the result, which is why --force is needed: it deletes the found unused files without asking.

bash
fengniao --exclude Carthage --force

This is the most aggressive way to use the tool, and the README's own recommendation is to exclude vendor folders when you do it. A build phase that deletes files on every build also means a mistake propagates to everyone who checks out the branch, not just to the person who ran the command. If you want a build-time signal without deletion, --xcode-warnings prints results as Xcode warnings and returns a non-zero code if any are found, which fits a CI check better than --force does.

The --skip-proj-reference flag is the other one to know about here. It skips cleaning the .pbxproj reference, leaving the project file untouched. The README suggests it when you build multiple projects with dependencies and want to keep .pbxproj unchanged while compiling. If your build phase runs against a project file you do not want rewritten, that flag is the difference between a read-only check and a mutating one.

Where FengNiao gets it wrong, and when not to use it

The name-matching approach is the main failure mode. An asset name built at runtime, returned from a server, or stored in a file type outside the default search set can be flagged as unused even though the app loads it. Running the tool with --force in that situation removes a live resource, and the README states the deletion cannot be restored.

A second constraint is that FengNiao edits the project file by default. Unless you pass --skip-proj-reference, it cleans the .pbxproj reference along with the files. That is convenient when you want the project tidy, and a liability when the .pbxproj is generated, shared across projects, or edited concurrently by other tools. The README acknowledges the multi-project dependency case explicitly, which suggests the author has seen it.

There is also a maintenance dimension. The last push to the repository was on 2026-05-09, and release 0.13.0 is dated the same day. The tool is not archived. Its README still references a Travis CI badge, which dates parts of the documentation, and the default extension lists reflect an era of .m and .mm files alongside .swift. None of that makes it broken, but it does mean you should check the defaults against your project rather than assume they match modern layouts.

Finally, FengNiao is not a general project slimmer. It does not find unused Swift types, unused localisation keys, or unused bundle files outside the folders it scans. Using it and expecting a full dead-code report will disappoint.

Alternatives and how their approach differs

The README itself points to a GUI app version of FengNiao, located in the App folder of the same repository. The README states that no pre-built binary is provided and that you must build it yourself. The difference from the command line tool is interface and workflow, not detection logic: it is the same project, and the README does not describe a different scanning mechanism for it.

For teams that want the check inside a build rather than as a separate step, the comparison is between FengNiao's own two modes. --force in a Run Script phase mutates the project during every build. --xcode-warnings surfaces the same findings as warnings and returns a non-zero code. The second is the closer analogue to a linter: it reports and fails, and leaves the decision to a human. If your team already treats warnings as errors, --xcode-warnings slots into that habit without giving a script permission to delete files.

Beyond that, the honest alternative is doing the work manually inside Xcode's asset catalog, or writing your own script around the same idea. FengNiao's value is that it already implements the extraction, the regex search and the subtraction, and it is small enough to read. If your resource naming follows conventions the defaults do not cover, a custom script with your own extension lists may be less risky than adjusting FengNiao's flags and hoping the defaults are close enough.

Licence, maintenance and the cost of keeping it around

FengNiao is released under the MIT licence, as stated in the README and in the repository's LICENSE file. MIT is permissive: you can use, modify and redistribute it, including in commercial projects, provided the licence and copyright notice are preserved. That is a general description of the licence text, not legal advice for your situation.

The practical upgrade cost is low. There is no runtime dependency to manage, no server, no configuration file in the repository layout. Installation is a binary in your PATH via Mint or a manual copy. Upgrading means reinstalling through Mint or rebuilding from source, and the release cadence visible in the repository is roughly a few releases per year, with 0.13.0 on 2026-05-09, 0.12.0 on 2026-03-06 and 0.11.0 on 2025-11-29.

The cost that matters is not the upgrade, it is the blast radius. A tool that deletes files and rewrites .pbxproj needs a clean working tree before every run. The README's warning about backups and version control is the real operating instruction here, and it is the one to enforce if you put FengNiao into a build phase.

Editorial conclusion

FengNiao fits teams with a large, old Xcode project whose asset catalogs have accumulated unused imagesets, jpgs, pngs, gifs and pdfs, and who already keep the project in version control. It is a poor fit for anyone working on a project without a clean git state, and for projects where resources are referenced in ways the scanner does not model, such as server-driven names. Before trusting it, run fengniao --list-only on a branch and inspect the output yourself, then try a run with --skip-proj-reference so the .pbxproj stays untouched while you evaluate the results.

Frequently asked questions

Can FengNiao delete an image that my app still uses?

Yes, if the resource name is not written literally in one of the searched file types. FengNiao extracts resource names and searches for them with a regular expression in files of type m, mm, swift, xib, storyboard and plist, so a name assembled at runtime or stored elsewhere may be classified as unused.

How do I see which files FengNiao would delete without deleting them?

Use the --list-only flag. The README states it lists unused files and exits without prompting, which makes it the safe way to inspect results before committing to a deletion run.

Does FengNiao modify my Xcode project file?

By default it cleans the .pbxproj reference as well as the resource files. The --skip-proj-reference flag skips that step and leaves the project file untouched, which the README suggests when building multiple projects with dependencies.

Official sources

  1. Issues
  2. License: MIT
  3. onevcat/FengNiao on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/onevcat-fengniao.svg)](https://hysenlabs.com/projects/onevcat-fengniao)