80

In Objective-C, you can print the call stack by doing the following:

NSLog(@"%@", [NSThread callStackSymbols]);

How do you do this in Swift without using Foundation class?

5
  • 2
    Autocompletion in playground gives me: NSLog(NSThread.callStackSymbols()).
    – Eendje
    Commented Jun 10, 2015 at 11:26
  • Sorry, I should clarify - without using Foundation class.
    – Boon
    Commented Jun 10, 2015 at 11:48
  • 2
    By using Foundation classes. It's not evil. Much of the power of Cocoa/Cocoa touch is built around the Foundation classes. Use them when needed.
    – Duncan C
    Commented Jun 10, 2015 at 11:53
  • @DuncanC Agree but want to know a way for a strictly Swift code.
    – Boon
    Commented Jun 13, 2015 at 1:40
  • There is no way I know of to do this without using Foundation classes. There is no way I know of to write an iOS or Mac OS GUI app without using Foundation Classes. You can call Foundation Class methods using purely Swift code, but you still need to use Foundation Classes.
    – Duncan C
    Commented Feb 18, 2016 at 13:42

7 Answers 7

131

As Jacobson says, use the following:

Swift 2:

print(NSThread.callStackSymbols())

Swift 3 / Swift 4:

print(Thread.callStackSymbols)

That's Swift code. It's using a Foundation method, but so does 90%+ of what you do on iOS.

EDIT:

Note that the formatting looks better if you use:

Thread.callStackSymbols.forEach{print($0)}

From the debugger command line you can type

e Thread.callStackSymbols.forEach{print($0)}
6
  • 1
    that works great, but I get a messed up layout in the console window, looks like some tab / whitespace issue. Using Xcode 7.2 Commented Feb 18, 2016 at 13:39
  • that seems to work, is there a way to get a more readable stack trace, like when you put a breakpoint in Xcode you can see the function calls with their parameters just like in the swift source file, but the 'dump' gives a less readable version with addresses etc Commented Feb 18, 2016 at 13:49
  • @AlexMan, As of Swift 3, yes. However, this post was made before Swift 3 was released.
    – Duncan C
    Commented Nov 30, 2016 at 15:50
  • 1
    I'm getting this: error: unknown command shorthand suffix: '(Thread.callStackSymbols)' error: Unrecognized command 'print(Thread.callStackSymbols)'.
    – Kingalione
    Commented Apr 12, 2017 at 14:12
  • @Kingalione, if you're trying to type it in the debugger, you need to type e print(Thread.callStackSymbols) or e Thread.callStackSymbols.forEach{print($0)}. e is short for expression, the LLDB command that tells the debugger to evaluate what you type as Swift statement.
    – Duncan C
    Commented Apr 12, 2017 at 15:20
14

For Swift 3 use:

print(Thread.callStackSymbols)

or for better formatting

for symbol: String in Thread.callStackSymbols {
    print(symbol)
}
1
  • 9
    or even more swift 3: Thread.callStackSymbols.forEach { print($0) } Commented Nov 30, 2016 at 9:15
9
 print(Thread.callStackSymbols.joined(separator: "\n"))

With this code, one can see the calls in different lines each.

 1   MyApp                               0x0000000100720780 $s9MyAppModule....
 2   CoreFoundation                      0x0000000181f04c4c EA9C1DF2-94C7-379B-BF8D-970335B1552F + 166988
 3   CoreFoundation                      0x0000000181f99554 EA9C1DF2-94C7-379B-BF8D-970335B1552F + 775508
 4   CoreFoundation                      0x0000000181f6eb34 EA9C1DF2-94C7-379B-BF8D-970335B1552F + 600884
 5   CoreFoundation                      0x0000000181f19754 _CFXNotificationPost + 696
 6   Foundation                          0x0000000183634138 86D8A58D-B71F-34C6-83E0-014B2B835F1D + 106808
1
  • 1
    While this code snippet may solve the question, including an explanation will help people understand the reasons for your code suggestion.
    – Gerhard
    Commented Feb 10, 2022 at 13:36
6

This improves the output a little.

for symbol: String in NSThread.callStackSymbols() {
    NSLog("%@", symbol)
}
1
  • 2
    Why not use Swift's print in the for loop to display each line rather than NSLog? NSLog is quite "noisy" (every line starts with a timestamp, info about the process and thread, etc.)
    – Duncan C
    Commented Aug 19, 2016 at 22:56
2

Here's a great utility class I found on github:

https://github.com/nurun/swiftcallstacktrace

You get a tuple (class,method) of any stack trace symbol so you can do a clean printout.

CallStackAnalyser.classAndMethodForStackSymbol(NSThread.callStackSymbols()[2])

Edit: swift 4.1 update

https://github.com/GDXRepo/CallStackParser

3
  • 1
    Unfortunately this doesn't work with Swift 3, and it looks like there is no easy way to "demangle" function names with Swift 3. Although this project might be a possibility (haven't tried it myself): github.com/mattgallagher/CwlDemangle
    – RenniePet
    Commented Dec 20, 2016 at 10:07
  • 1
    looks like someone made some effort to make it work with newer versions of Swift: github.com/GDXRepo/CallStackParser
    – CMash
    Commented Aug 2, 2020 at 19:41
  • Doesn't work with Swift 5+ Commented Aug 17, 2023 at 9:15
2

Thread.callStackSymbols() is nice to have. But the traceback is ugly. Demangling would be nice. The Swift 4.1+ demangler linked in @MikeS's answer is extremely comprehensive and impressive, but it's also over 4000 lines of code, overkill if you just need app, class and method, and it's quite a lot to add to a project, which I'd prefer to not to risk forgetting not to ship my app with :-)

This is a quick prototype of something that does some basic demangling of appname, class and method (which are the easy part to figure out). It's not polished. For example, it doesn't check for nil/failures in the regex ops, since it just gets a line from the callstack, which should be consistent enough to avoid problems. However, improved versions of it are welcome answers.

I added it to a class I named Debug, where I keep other debugging stuff, and invoke it from wherever in my app as:

Debug.whence()

    ... note: "where" is a Swift reserved word, and whence means basically the same thing.

It prints a line of this form (only one line, not full stack):

EventEditorViewController.configureNavigationItem():419 
I'll probably add an argument to take an optional object arg and then do a refined display of the object and its address without some of the parameters and syntax swift's builtin obj dump logging does, so that it would display obj info and where it is being traced.

This probably can only parse lines inside the app. It probably can't demangle non-Swift calls (like Foundation), not sure. If you need a more comprehensive demangler, check @MikeS's answer.

static func whence(_ lineNumber: Int = #line) {
    
    func matchRegex(_ matcher: String,  string : String) -> String? {
        let regex = try! NSRegularExpression(pattern: matcher, options: [])
        let range = NSRange(string.startIndex ..< string.endIndex, in: string)
        guard let textCheckingResult = regex.firstMatch(in: string, options: [], range: range) else {
            return nil
        }
        return (string as NSString).substring(with:textCheckingResult.range(at:1)) as String
    }

    func singleMatchRegex(_ matcher: String,  string : String) -> String? {
        let regex = try! NSRegularExpression(pattern: matcher, options: [])
        let range = NSRange(string.startIndex ..< string.endIndex, in: string)
        let matchRange = regex.rangeOfFirstMatch(in: string, range: range)
        if matchRange == NSMakeRange(NSNotFound, 0) {
            return nil
        }
        return (string as NSString).substring(with: matchRange) as String
    }

    var string = Thread.callStackSymbols[1]
    string = String(string.suffix(from:string.firstIndex(of: "$")!))
                    
    let appNameLenString = matchRegex(#"\$s(\d*)"#, string: string)!
    let appNameLen = Int(appNameLenString)!

    string = String(string.dropFirst(appNameLenString.count + 2))
    
    let appName = singleMatchRegex(".{\(appNameLen)}", string: string)!
    
    string = String(string.dropFirst(appNameLen))
    
    let classNameLenString = singleMatchRegex(#"\d*"#, string: string)!
    let classNameLen = Int(classNameLenString)!

    string = String(string.dropFirst(classNameLenString.count))
    
    let className = singleMatchRegex(".{\(classNameLen)}", string: string)!

    string = String(string.dropFirst(classNameLen))
    
    let methodNameLenString = matchRegex(#".(\d*)"#, string: string)!
    let methodNameLen = Int(methodNameLenString)!

    string = String(string.dropFirst(methodNameLenString.count + 1))
    
    let methodName = singleMatchRegex(".{\(methodNameLen)}", string: string)!
    
    let _ = appName
    print("\(className).\(methodName)():\(lineNumber)")
}
0
1

I needed to write the callstack to a log file so I tweaked it like so.

var ErrorStack = String()
Thread.callStackSymbols.forEach {
    print($0)
    ErrorStack = "\(ErrorStack)\n" + $0
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.