How to Use `swift-demangle` to Decode Symbolicated Crash Logs for Swift
Crash logs are not supposed to be cryptic. Yet every Swift developer has stared at a stack frame like _$s9MyAppName10DetailViewV4bodyQrvg and felt the distinct urge to close the laptop and take up pottery. That string is not noise. It is a mangled Swift symbol, and swift-demangle is the tool that turns it back into something a human can read.
This article covers what mangling is, when Xcode already handles it for you, when you need to reach for swift-demangle manually, and how to read the output without guessing.
Why Swift symbols look like that
Swift uses name mangling to encode a declaration’s module, type context, function name, argument types, return type, and various attributes into a single flat string. The Swift compiler repository documents this as the stable mangling scheme, which produces unique symbols for ABI-public declarations (Swift compiler documentation index). The scheme exists because the linker and runtime need globally unique names, and because Swift supports overloading, generics, and nested types in ways that C-style symbol names cannot express.
The practical consequence: a symbol like _$s9MyAppName10DetailViewV4bodyQrvg is not random. It is a structured encoding. The $s prefix identifies the Swift 5+ mangling scheme, and the rest is a tree of nodes describing the declaration. The demangler in lib/Demangling/Demangler.cpp parses that string into a node tree and renders it back as readable Swift (Demangler.cpp).
You do not need to memorize the encoding. You need to know how to invoke the tool that reverses it.
What Xcode already does for you
When Xcode has the matching dSYM file for a build, it symbolicates crash reports automatically. Apple’s documentation on adding identifiable symbol names to a crash report describes this pipeline: Xcode uses the dSYM to map addresses back to function names, and for Swift symbols it demangles them as part of that process. If you are looking at a crash in Xcode’s organizer and the frames show readable Swift names, the demangling has already happened.
The JSON format of a crash report, documented by Apple in interpreting the JSON format of a crash report, stores symbol information in fields that can contain either mangled or demangled names depending on whether symbolication succeeded. When symbolication fails — missing dSYM, mismatched UUID, stripped binary — you get raw addresses or mangled names, and that is when manual demangling becomes necessary.
When you actually need swift-demangle
There are four situations where the automatic pipeline does not save you:
- You have a mangled name but no dSYM. Maybe you received a crash log from a user, or you are inspecting a binary you did not build. The name is there, but nothing has demangled it.
- You are reading raw symbol tables. Tools like
nm,otool, orllvm-nmoutput mangled names by default. If you are auditing what symbols a binary exports, you will see the mangled form. - You are writing tooling. If you are building a crash-report processor, a symbolication service, or a CI step that analyzes binaries, you will need to demangle programmatically or via the command line.
- You want to verify what a symbol actually refers to. Sometimes the demangled form reveals that the crashing frame is a generic specialization, a thunk, or a closure that you did not expect.
Basic usage
The tool ships with the Swift toolchain. On macOS with Xcode installed, it is available at xcrun swift-demangle. You can also find it directly in the toolchain’s usr/bin directory.
The simplest invocation takes a mangled name as an argument:
xcrun swift-demangle '_$s9MyAppName10DetailViewV4bodyQrvg'
The output is the demangled form. For a symbol like the one above, you would see something resembling:
MyAppName.DetailView.body.getter : ()
The exact rendering depends on the symbol’s structure, but the key point is that the module name, type name, property or method name, and type information are all recovered.
You can pass multiple symbols in one invocation:
xcrun swift-demangle '_$s9MyAppName10DetailViewV4bodyQrvg' '_$s9MyAppName10DetailViewV4bodyQrvgTY0_'
And you can pipe input from stdin, which is useful when you are processing a list of symbols:
nm MyApp.app/MyApp | xcrun swift-demangle
Useful flags
The tool accepts several flags that change its behavior. The most useful ones for crash-log work are:
--compact— produces a more compact output, omitting some type information. Useful when you only need to identify the function, not its full signature.--simplified— simplifies the demangled output by removing some redundant type information. This can make deeply nested generic symbols more readable.--no-synthesize— prevents the demangler from synthesizing names for symbols that lack explicit names, such as certain compiler-generated thunks.--classify— prefixes the output with a classification of the symbol type (function, type metadata, etc.).
For a full list, run xcrun swift-demangle --help. The flags are stable across recent toolchain versions, but the exact set can vary slightly between Swift releases.
Reading the output
Demangled output follows Swift’s naming conventions. A few patterns are worth recognizing:
- Module.Type.method — a method on a type in a module. The module name is the first component.
- getter / setter — property accessors. A crash in a
gettermeans the property access itself faulted, which often points to a force-unwrap or an out-of-bounds access inside the getter. - closure #1 in … — a closure. The number identifies which closure in the enclosing function.
- specialized … — a generic specialization. The compiler generated a concrete version of a generic function for specific types. This is normal and often means the crash is in code you wrote, just compiled with concrete types.
- thunk for … — a compiler-generated adapter, often for Objective-C interop or protocol witness tables. A crash in a thunk usually means the real problem is in the function the thunk calls.
If the demangled name includes @objc or references an Objective-C selector, the frame may be crossing the Swift/Objective-C boundary. That is a common source of crashes that look mysterious until you see the boundary in the symbol name.
Mangling scheme versions
Swift has had multiple mangling schemes. The current stable scheme uses the $s prefix (or _$s on Mach-O, where the leading underscore is added by the platform). Older schemes used _T0 for Swift 4 and $S for Swift 4.x. The demangler source lists these prefixes explicitly (Demangler.cpp).
If you are looking at a crash log from an older build, you may see _T0 symbols. swift-demangle handles them. You do not need to do anything special. The tool detects the prefix and applies the appropriate parsing rules.
One edge case: symbols that begin with _T but are not Swift symbols. The demangler checks for the old function type mangling prefix _T specifically, and the isSwiftSymbol function in the demangler source returns true for those. If you pass a non-Swift symbol, the tool will either fail to demangle it or return it unchanged. That is expected behavior, not a bug.
Demangling in the JSON crash report
Apple’s JSON crash report format includes symbol information in a structured way. When you open a .ips file, you will see fields like symbol and symbolLocation inside the threads array. If symbolication succeeded, symbol contains the demangled name. If it failed, you may see a mangled name or a raw address.
If you are processing these files programmatically, you can extract the mangled names and pipe them through swift-demangle. A common pattern is to use jq to extract the symbol fields, then pass them to the demangler:
jq -r '.threads[].frames[].symbol // empty' crash.ips | xcrun swift-demangle
This is not a substitute for proper symbolication with a dSYM. It is a fallback when you have the names but not the symbols, or when you want to verify what a symbol refers to without opening Xcode.
A practical workflow
Here is a sequence that works when you have a crash log and need to understand a frame:
- Open the crash log. If it is a
.ipsfile, you can read it as JSON or open it in Xcode. - Identify the frame you care about. If the symbol is already readable, you are done.
- If the symbol is mangled, copy it and run
xcrun swift-demangle '<symbol>'. - Read the demangled name. Identify the module, type, and function.
- If the name includes
specializedorthunk, look at the next frame down. The real crash is often in the function that the thunk or specialization calls. - If you need the full signature, omit
--compactand--simplified. If you only need the function name, use them.
This workflow does not replace proper symbolication. It supplements it. The dSYM is still the authoritative source for mapping addresses to source locations. swift-demangle only translates names.
What swift-demangle does not do
It does not symbolicate. It does not map addresses to source lines. It does not need a dSYM. It takes a mangled name and returns a readable name. That is the entire contract.
If you have a crash log with raw addresses and no symbols, swift-demangle will not help you. You need the dSYM and a symbolication tool. Xcode’s symbolicatecrash script, or atos, are the tools for that job. swift-demangle is for when you already have the name and need to read it.
FAQ
Where is swift-demangle located?
It ships with the Swift toolchain. On macOS, use xcrun swift-demangle to invoke the version bundled with your selected Xcode. You can also find it directly in the toolchain’s usr/bin directory.
Can I demangle a symbol without Xcode?
Yes, if you have a Swift toolchain installed. On Linux, the tool is available as part of the Swift toolchain package. The command is swift-demangle rather than xcrun swift-demangle.
Why does the demangled output still look strange?
Some symbols are compiler-generated and do not correspond to a single source declaration. Thunks, specializations, and closure names are examples. The demangled form is accurate; it just describes something the compiler created rather than something you wrote explicitly.
Does swift-demangle work on Objective-C symbols?
No. It is specific to Swift mangling. Objective-C symbols use a different convention and are already readable in most cases. If you pass an Objective-C symbol, the tool will not demangle it.
What is the difference between –compact and –simplified?
--compact reduces the overall length of the output by omitting some type information. --simplified removes redundant type information that the demangler would otherwise include. Both make output shorter, but they do so in different ways. For crash-log triage, either is usually fine. For understanding a complex generic signature, neither is ideal.
Can I use swift-demangle in a script?
Yes. It reads from stdin and writes to stdout, so it composes well with other tools. The exit code is zero on success and non-zero on failure, which makes it usable in shell scripts and CI pipelines.
The bottom line
swift-demangle is a small tool with a narrow job. It does that job well. When Xcode’s symbolication pipeline works, you never need it. When it does not — missing dSYM, raw symbol tables, custom tooling — it is the difference between a stack trace you can read and a stack trace you cannot.
Keep it in your toolbox. You will not need it often, but when you do, you will be glad it is there.