Swift: determine what object called a function? - swift

I am writing some Swift code and I would like to know the class of the object that called the function. I don't want to pass in any parameters. From within the function I want to be able to know who called it.
Any suggestion?

If you want to do that using Swift, you can do this:
func debug(file: String = #file, line: Int = #line, function: String = #function) -> String {
return "\(file):\(line) : \(function)"
}

To access the underlying class of a method from within itself, use the dynamicType property:
self.dynamicType
If you want to know the origin of the original call, you can use NSThread to return debugging information about the stack:
NSThread.callStackSymbols()
This method returns a descriptive array of values that you're used to seeing when exceptions are thrown. The strings represent a backtrace of all current activity on your call stack.
I don't want to be presumptuous, but it seems to me that outside of debugging, there isn't a good reason, conceptually, at least, to know the origin of a specific method call for any and every function. If you need to retrieve the class Type of the last method call on the stack, why not implement an interface that lets you access this information through a straightforward route?

You can use following template to know from which file, line number in file, and function this someFunction is called:
func someFunction(file: String = #file, line: Int = #line, function: String = #function)
{
NSLog("\(file.lastPathComponent):\(line) : \(function)")
}

Swift:
Add a (sender: Anyobject) as parameter to that function, and then print the sender (the function caller) like this:
func yourFunc(sender: AnyObject){
print(sender)
}
Or add a symbolic breakpoint with a po thread to see the stack trace of the caller of the method

The final way, use the follow protocol:
protocol PrefixPrint {
func formatPrint(_ text: String, function: String, line: Int) -> Void
}
extension PrefixPrint {
#inline(__always)
func formatPrint(_ text: String, function: String = #function, line: Int = #line) {
#if DEBUG
print("\(self).\(function).\(line): \(text)")
#endif
}
}
Console like: FootBall.VNSocketManager.init().44: xx

Joining together a couple of answers and comments:
func function(file: String = #file) {
if let url = URL(string: file) {
let className = url.deletingPathExtension().lastPathComponent
print(className)
}
}

Related

Assigning value to a variable from an asynchronous function

I am trying to add a variable in the String class, of type Bool, that if used on a userName as String, is supposed to check Firebase and return true if the username exists and false if not. In the end I am aiming to use it like this:
username.isUserNameAvailable
The problem is that my Firebase function returns data asynchronously. Therefore in my function below, variable isTaken, is assigned a value after variable availability is returned from the function. Is there a way to fix this?
fileprivate extension String {
var isUserNameAvailable : Bool {
var availability : Bool?
DatabaseManager.system.isUserNameTaken(userName: self, completion: {(isTaken) in
availability = !isTaken
print("isTaken = \(isTaken)")
})
print("availability = \(availability)")
return availability!
}
}
You shouldn't try to treat an asynchronous call as something that is already available.
I would suggest having a method that calls a code block when the Firebase request is finished.
Something like this:
fileprivate extension String {
func isUserNameAvailable(completion: (Bool)->()) {
DatabaseManager.system.isUserNameTaken(userName: self, completion:{(isTaken) in
completion(!isTaken)
})
}
}
You would call it like this:
string.isUserNameAvailable { (available) in
//use the variable
}
It's not exactly what you wanted, but it still simplifies the call while showing a bit more clearly that the call is asynchronous.

custom console log in swift [duplicate]

I am writing some Swift code and I would like to know the class of the object that called the function. I don't want to pass in any parameters. From within the function I want to be able to know who called it.
Any suggestion?
If you want to do that using Swift, you can do this:
func debug(file: String = #file, line: Int = #line, function: String = #function) -> String {
return "\(file):\(line) : \(function)"
}
To access the underlying class of a method from within itself, use the dynamicType property:
self.dynamicType
If you want to know the origin of the original call, you can use NSThread to return debugging information about the stack:
NSThread.callStackSymbols()
This method returns a descriptive array of values that you're used to seeing when exceptions are thrown. The strings represent a backtrace of all current activity on your call stack.
I don't want to be presumptuous, but it seems to me that outside of debugging, there isn't a good reason, conceptually, at least, to know the origin of a specific method call for any and every function. If you need to retrieve the class Type of the last method call on the stack, why not implement an interface that lets you access this information through a straightforward route?
You can use following template to know from which file, line number in file, and function this someFunction is called:
func someFunction(file: String = #file, line: Int = #line, function: String = #function)
{
NSLog("\(file.lastPathComponent):\(line) : \(function)")
}
Swift:
Add a (sender: Anyobject) as parameter to that function, and then print the sender (the function caller) like this:
func yourFunc(sender: AnyObject){
print(sender)
}
Or add a symbolic breakpoint with a po thread to see the stack trace of the caller of the method
The final way, use the follow protocol:
protocol PrefixPrint {
func formatPrint(_ text: String, function: String, line: Int) -> Void
}
extension PrefixPrint {
#inline(__always)
func formatPrint(_ text: String, function: String = #function, line: Int = #line) {
#if DEBUG
print("\(self).\(function).\(line): \(text)")
#endif
}
}
Console like: FootBall.VNSocketManager.init().44: xx
Joining together a couple of answers and comments:
func function(file: String = #file) {
if let url = URL(string: file) {
let className = url.deletingPathExtension().lastPathComponent
print(className)
}
}

How to determine which object/class is calling a global Swift function?

With this code:
func externalFunc() {
println("How can I know which object/class is calling me?")
}
class Test {
func callExternalFunc() {
externalFunc()
}
}
In the Objective-C runtime objc_msgSend passes two hidden parameters to every message we send. They are self and _cmd. (Source)
In the above example, is there any way to know who is calling externalFunc?
I'm not sure if there is a way to obtain this automatically, but you can get this info if you add a default param of type String to the function and set it to #function.
For example...
func externalFunc(callingFunctionName: String = #function) {
println("Calling Function: \(callingFunctionName)")
}
Then you would call it without the added default param...
let test = Test()
test.callExternalFunc()
And it would print the following...
"Calling Function: callExternalFunc()"
If you are willing to modify the method signature you could do something like below:
func externalFunc(file: String = #file, line: Int = #line) {
print("calling File:\(file) from Line:\(line)")
}
From apple's swift blog
Swift borrows a clever feature from the D language: these identifiers
(__FILE__ & __LINE__ ) expand to the location of the caller when
evaluated in a default argument list.
Note that __FILE__ and __LINE__ have been depreciated in Swift 2.2 and have been removed in Swift 3. They are replaced by #file, and #line.
Here's a great utility class I found on github:
https://github.com/nurun/swiftcallstacktrace
Use like this:
let callingMethodInfo = CallStackAnalyser.getCallingClassAndMethodInScope(false)
if let callingMethodInfo = callingMethodInfo {
NSLog("class: %#", callingMethodInfo.0)
NSLog("method: %#", callingMethodInfo.1)
}
In your question you are mentioning self and _cmd.
self is accessible in Swift exactly in the same way as in Obj-C (which is logical).
_cmd (selector of the current method) is not accessible. There is no reason for it to be accessible, Swift doesn't use selectors outside Obj-C contexts (in pure Swift you cannot call selectors dynamically). The only use case for it is to print the name of the current function for debugging purposes. The same can be achieved in Obj-C (or C) using __FUNCTION__ macro. The same can be achieved in Swift:
func getCurrentFunctionName(functionName: StaticString = #function) -> String {
return String(functionName)
}
func externalFunc() {
print("Function name: \(getCurrentFunctionName())") // prints "externalFunc"
}
Note that in your example externalFunc is a function, not a method. Even in Obj-C neither self or _cmd wouldn't be available for it.
If you want to know who has called your method (and I really suppose you want to know it for debugging purposes), then you can inspect your call stack:
func externalFunc() {
let stackSymbols = NSThread.callStackSymbols()
stackSymbols.forEach {
let components = $0.stringByReplacingOccurrencesOfString(
"\\s+",
withString: " ",
options: .RegularExpressionSearch,
range: nil
).componentsSeparatedByString(" ")
let name = components[3]
let demangledName = _stdlib_demangleName(name)
print("\(demangledName)")
}
}
which prints (for my project called SwiftTest):
SwiftTest.externalFunc () -> ()
SwiftTest.Test.callExternalFunc () -> ()
main
start
0x0

Swift function alias

How can I create an alias for a function in swift?
For example
I want to call
LocalizedString("key")
and it should call
NSLocalizedString("key", comment:"")
I saw typealias command but it looks like it works only for types.
Functions are named closures, so you can just assign a function to a variable:
let LocalizedString = NSLocalizedString
You can create pseudo-aliases for class/struct methods as well. Each method is actually a static (class) curried function, taking a class instance as its first parameter. So given a class:
class MyClass {
var data: Int
init(data: Int) {
self.data = data
}
func test() {
println("\(data)")
}
}
you can assign the test method to a variable:
let test = MyClass.test
and then invoke it as:
var instance = MyClass(data: 10)
test(instance)()
UPDATE
I've just realized that I missed one important detail in your question: you want to hide the comment parameter. And my proposed solution doesn't allow that, whereas #rintaro's solution does.
However I use a different approach for that: I create a String extension implementing a computed property:
extension String {
var localized: String {
return NSLocalizedString(self, comment: "")
}
}
and then I can just call it on any string variable or literal:
var string = "test_resource"
string.localized
"another_resource".localized
The shortest one is:
let LocalizedString = { NSLocalizedString($0, comment:"") }
But, it's actually a new function. Just wrapping NSLocalizedString.
Maybe you can use undocumented #transparent attribute. It inlines function call. see this topic on Developer Forum.
#transparent LocalizedString(key:String) -> String {
return LocalizedString(key, comment:"")
}
But it's not recommended. Moreover, as long as my tests, all of following codes eventually emit exact the same LLVM IR code with -O optimization.
script1: with #transparent
import Foundation
#transparent func LocalizedString(key:String) -> String {
return LocalizedString(key, comment:"")
}
println(LocalizedString("key"))
script2: without #transparent
import Foundation
func LocalizedString(key:String) -> String {
return LocalizedString(key, comment:"")
}
println(LocalizedString("key"))
script3: Direct NSLocalizedString call
import Foundation
func LocalizedString(key:String) -> String {
return LocalizedString(key, comment:"")
}
println(NSLocalizedString("key", comment:""))
All of above are inlined to perform direct NSLocalizedString call.
But, the following code emits different:
script4: Closure wrapping
import Foundation
let LocalizedString = { NSLocalizedString($0, comment:"") }
println(NSLocalizedString("key", comment:""))
It's also inlined, but additional refcount instruction to LocalizedString is inserted.
So, as a conclusion, you should simply use this:
func LocalizedString(key:String) -> String {
return LocalizedString(key, comment:"")
}

Macros in Swift?

Does Swift currently support macros, or are there future plans to add support? Currently I'm scattering:
Log.trace(nil, function: __FUNCTION__, file: __FILE__, line: __LINE__)
In various places throughout my code.
In this case you should add a default value for the "macro" parameters.
Swift 2.2 and higher
func log(message: String,
function: String = #function,
file: String = #file,
line: Int = #line) {
print("Message \"\(message)\" (File: \(file), Function: \(function), Line: \(line))")
}
log("Some message")
Swift 2.1 and lower
func log(message: String,
function: String = __FUNCTION__,
file: String = __FILE__,
line: Int = __LINE__) {
print("Message \"\(message)\" (File: \(file.lastPathComponent), Function: \(function), Line: \(line))")
}
log("Some message")
This is what fatalError and assert functions do.
There are no other macros except the conditional compilation already mentioned in another answer.
The Apple docs state that:
Declare simple macros as global constants, and translate complex macros into functions.
You can still use #if/#else/#endif - but my feeling is that they will not introduce macro functions, the language simply doesn't need it.
Since XCode 7.3, the __FILE__ __FUNCTION__ and __LINE__ compile-time constants have become the nicer-looking #file #function and #line respectively.
Here is an updated Swift 2 answer.
func LogW(msg:String, function: String = #function, file: String = #file, line: Int = #line){
print("[WARNING]\(makeTag(function, file: file, line: line)) : \(msg)")
}
private func makeTag(function: String, file: String, line: Int) -> String{
let url = NSURL(fileURLWithPath: file)
let className = url.lastPathComponent ?? file
return "\(className) \(function)[\(line)]"
}
Example of use:
LogW("Socket connection error: \(error)")
lastPathComponent needs an NSURL, so I changed the above code to this:
func log(message: String,
function: String = __FUNCTION__,
file: String = __FILE__,
line: Int = __LINE__) {
let url = NSURL(fileURLWithPath: file)
print("Message \"\(message)\" (File: \(url.lastPathComponent ?? "?"), Function: \(function), Line: \(line))")
}
log("some message")
There is way to use macros on swift (but this used in Mixed of objective c and swift)
declare your macros into Project-name-Bridging-Header.h
#define YOUR_MACRO #"Description"
or create separate header file for macros "macros.h"
import this header "macros.h" in to your Bridging-Header.h file..
now just save your project your macros will came in swift file ..
if you don't wanna object c code on your swift project... just create dummy cocoa touch classes it will create bridging header then use my way...
Macros are evil, but sometimes you just need them. For example, I have
struct RegionEntity {
var id: Int!
}
And I want to place instances of this struct to Set. So I have to conform it to Hashable protocol.
extension RegionEntity: Hashable {
public var hashValue: Int {
return id
}
}
public func ==(first: RegionEntity, second: RegionEntity) -> Bool {
return first.id == second.id
}
Great. But what if I have dozens of such structs and the logic is the same? Maybe I can declare some protocol and conform it to Hashable implicitly. Let's check:
protocol Indexable {
var id: Int! { get }
}
extension Indexable {
var hashValue: Int {
return id
}
}
func ==(first: Indexable, second: Indexable) -> Bool {
return first.id == second.id
}
Well, it works. And now I'm gonna conform my struct to both protocols:
struct RegionEntity: Indexable, Hashable {
var id: Int!
}
Nope. I can't do that, because Equatable requires == operator with Self and there is no == operator for RegionEntity.
Swift forces me to copy-paste confirmation code for each struct and just change the name. With macro I could do that with only one line.
A macro proposal is going through Swift Evolution right now. This would allow you to define a custom type that could create a macro that can be evaluated at compile time, like the C stringify macro, for example.
https://forums.swift.org/t/se-0382-expression-macros/62090