If anyone tells you two of your apps are the same app, that is a claim about bytes and you can settle it in a minute. Hash every source file in every project, then count how many are byte-identical across two different apps. I ran it over sixteen app directories and got 1269 files, 2 shared. Two shared files between separate products is not a number you want to be wrong about, in either direction.
import hashlib, collections
from pathlib import Path
SKIP = (".build", "DerivedData", ".git")
by_hash = collections.defaultdict(set)
for app in Path("apps").iterdir():
if not app.is_dir():
continue
for f in app.rglob("*.swift"):
if any(p in f.parts for p in SKIP):
continue
by_hash[hashlib.sha256(f.read_bytes()).hexdigest()].add(app.name)
shared = [a for a in by_hash.values() if len(a) > 1]
print(len(shared)) # 2Both hits were the same filename: GeneratedAssetSymbols.swift, a thirty-line file Xcode writes from your asset catalogue. Neither was mine. They matched because one compiler generated both from similar inputs, and they sat in directories named .derivedData-ui-glass, .derivedData-device, and plain build. My exclude list matched DerivedData, capital D. It never looked at any of them.
SKIP = (".build", "DerivedData", ".git", "build")
def is_generated(f):
return any(p.startswith(".derivedData") or p in SKIP for p in f.parts)- Step 01
Run it once and distrust the number
Whatever it prints, treat it as a first draft. The interesting output is not the count.
- Step 02
Print the path behind every match
Read them. A generated file, a vendored dependency, or a test fixture is not your code and should not be counted as yours.
- Step 03
Widen the exclude to a prefix, then re-run
Match .derivedData* rather than one spelling, and add plain build. Under-excluding invents duplicates you never wrote; over-excluding hides real ones. Now the count is quotable.