├── CallStackAnalyser.swift └── README.md /CallStackAnalyser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CallStackAnalyser.swift 3 | // 4 | // Created by Mitch Robertson on 2016-05-20. 5 | // Copyright © 2016 Mitch Robertson. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | class CallStackAnalyser { 11 | 12 | private static func cleanMethod(method:(String)) -> String { 13 | if (method.characters.count > 1) { 14 | let firstChar:Character = method[method.startIndex] 15 | if (firstChar == "(") { 16 | return method.substringWithRange(Range(start: method.startIndex.advancedBy(1), end: method.endIndex.advancedBy(0))) } 17 | } 18 | return method 19 | } 20 | 21 | /** 22 | Takes a specific item from 'NSThread.callStackSymbols()' and returns the class and method call contained within. 23 | 24 | - Parameter stackSymbol: a specific item from 'NSThread.callStackSymbols()' 25 | - Parameter includeImmediateParentClass: Whether or not to include the parent class in an innerclass situation. 26 | 27 | - Returns: a tuple containing the (class,method) or nil if it could not be parsed 28 | */ 29 | static func classAndMethodForStackSymbol(stackSymbol:String, includeImmediateParentClass:Bool? = false) -> (String,String)? { 30 | let components = stackSymbol.stringByReplacingOccurrencesOfString( 31 | "\\s+", 32 | withString: " ", 33 | options: .RegularExpressionSearch, 34 | range: nil 35 | ).componentsSeparatedByString(" ") 36 | 37 | if (components.count >= 4) { 38 | var packageClassAndMethodStr = _stdlib_demangleName(components[3]) 39 | packageClassAndMethodStr = packageClassAndMethodStr.stringByReplacingOccurrencesOfString( 40 | "\\s+", 41 | withString: " ", 42 | options: .RegularExpressionSearch, 43 | range: nil 44 | ).componentsSeparatedByString(" ")[0] 45 | 46 | var packageClassAndMethod = packageClassAndMethodStr.componentsSeparatedByString(".") 47 | let numberOfComponents = packageClassAndMethod.count 48 | 49 | if (numberOfComponents >= 2) { 50 | 51 | let method = CallStackAnalyser.cleanMethod(packageClassAndMethod[numberOfComponents-1]) 52 | if includeImmediateParentClass != nil { 53 | if (includeImmediateParentClass == true && numberOfComponents >= 4) { 54 | return (packageClassAndMethod[numberOfComponents-3]+"."+packageClassAndMethod[numberOfComponents-2],method) 55 | } 56 | } 57 | return (packageClassAndMethod[numberOfComponents-2],method) 58 | } 59 | } 60 | return nil 61 | } 62 | 63 | /** 64 | Analyses the 'NSThread.callStackSymbols()' and returns the calling class and method in the scope of the caller. 65 | 66 | - Parameter includeImmediateParentClass: Whether or not to include the parent class in an innerclass situation. 67 | 68 | - Returns: a tuple containing the (class,method) or nil if it could not be parsed 69 | */ 70 | static func getCallingClassAndMethodInScope(includeImmediateParentClass:Bool? = false) -> (String,String)? { 71 | let stackSymbols = NSThread.callStackSymbols() 72 | if (stackSymbols.count >= 3) { 73 | return CallStackAnalyser.classAndMethodForStackSymbol(stackSymbols[2], includeImmediateParentClass: includeImmediateParentClass) 74 | } 75 | return nil 76 | } 77 | 78 | /** 79 | Analyses the 'NSThread.callStackSymbols()' and returns the current class and method in the scope of the caller. 80 | 81 | - Parameter includeImmediateParentClass: Whether or not to include the parent class in an innerclass situation. 82 | 83 | - Returns: a tuple containing the (class,method) or nil if it could not be parsed 84 | */ 85 | static func getThisClassAndMethodInScope(includeImmediateParentClass:Bool? = false) -> (String,String)? { 86 | let stackSymbols = NSThread.callStackSymbols() 87 | if (stackSymbols.count >= 2) { 88 | return CallStackAnalyser.classAndMethodForStackSymbol(stackSymbols[1], includeImmediateParentClass: includeImmediateParentClass) 89 | } 90 | return nil 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # swiftcallstacktrace 2 | This is a utility class for parsing the current call stack in scope. This can be very useful for logging so that you automatically prefix logs with code contextual information. 3 | 4 | Here is a code sample that utilises the CallStackAnalyser with comments indicating the output. 5 | ```swift 6 | import UIKit 7 | 8 | class ViewController: UIViewController { 9 | 10 | override func viewDidLoad() { 11 | super.viewDidLoad() 12 | 13 | let innerClass = MyInnerClass() 14 | innerClass.publicInsideInnerClass() 15 | } 16 | 17 | class MyInnerClass { 18 | 19 | func publicInsideInnerClass() { 20 | anotherMethodInsideInnerClass() 21 | } 22 | 23 | private func anotherMethodInsideInnerClass() { 24 | 25 | let thisMethodInfo = CallStackAnalyser.getThisClassAndMethodInScope(true) 26 | 27 | if let thisMethodInfo = thisMethodInfo { 28 | NSLog("class: %@", thisMethodInfo.0) 29 | NSLog("method: %@", thisMethodInfo.1) 30 | 31 | /* 32 | class: ViewController.MyInnerClass 33 | method: anotherMethodInsideInnerClass 34 | */ 35 | } 36 | 37 | let callingMethodInfo = CallStackAnalyser.getCallingClassAndMethodInScope(false) 38 | 39 | if let callingMethodInfo = callingMethodInfo { 40 | NSLog("class: %@", callingMethodInfo.0) 41 | NSLog("method: %@", callingMethodInfo.1) 42 | 43 | /* 44 | class: MyInnerClass 45 | method: publicInsideInnerClass 46 | */ 47 | } 48 | 49 | // NB: passing 'true' for 'includeImmediateParentClass' would return the same thing since 'ViewController' is not an inner class 50 | let originalMethodInfo = CallStackAnalyser.classAndMethodForStackSymbol(NSThread.callStackSymbols()[2]) 51 | 52 | if let originalMethodInfo = originalMethodInfo { 53 | NSLog("class: %@", originalMethodInfo.0) 54 | NSLog("method: %@", originalMethodInfo.1) 55 | 56 | /* 57 | class: ViewController 58 | method: viewDidLoad 59 | */ 60 | } 61 | } 62 | } 63 | } 64 | ``` 65 | --------------------------------------------------------------------------------