Get Function Name in Swift

How to print out the method name and line number in swift

Literal        Type     Value

#file String The name of the file in which it appears.
#line Int The line number on which it appears.
#column Int The column number in which it begins.
#function String The name of the declaration in which it appears.
#dsohandle UnsafeMutablePointer The dso handle.

Example

print("Function: \(#function), line: \(#line)") 

With default values in parameters you can also create a function

public func track(_ message: String, file: String = #file, function: String = #function, line: Int = #line ) { 
print("\(message) called from \(function) \(file):\(line)")
}

which can be used like this

track("enters app")

In Swift 2.1

 Literal        Type     Value

__FILE__ String The name of the file in which it appears.
__LINE__ Int The line number on which it appears.
__COLUMN__ Int The column number in which it begins.
__FUNCTION__ String The name of the declaration in which it appears.

for more info see the documentation

Swift: Print name of a function stored in a variable

Swift is a statically dispatched programming language. This results in Swift using memory addresses as much as possible when it needs to call a function. The side effect is the inability to capture the called function name, since in most of the cases it will be a simple memory address.

#function works because this the construct gets replaced at compile time by the caller function (it's not a runtime construct).

If you have the debug symbols available, you could reconstruct the function name from a binary address, however this would require access to the dSYM infrastructure from within your application, which it's unlikely that you'll want to do, since shipping you app along with the debug symbols is an invitation for hackers to reverse engineer with your app.

Dynamically dispatched languages, like Objective-C, keep a reference to the called function (selector), but only if the called function is a method (i.e. a member function). Other languages, like Ruby, JavaScript, are interpreted languages, which makes the function name available at all times.

Calling function by name / string in swift

Try to change it to @objc func getDocumentsFolder() -> String

Swift language uses Table Dispatch for all class methods. But for performing selector on method, this method should be invoked with Message Dispatch.

That's what @objc keyword is doing - it's marks function as dynamic and it will be invoked with Message Dispatch

You can read more about method dispatches here

UPD

Agree with @Hamish comment, @objc will not make this function method dispatch for Swift, but for perform(_:) method.

Is there a way to get line number and function name in Swift language?

The Swift Language Reference defines a few "special literals" that offer this behavior:
































LiteralTypeValue
#fileStringThe name of the file in which it appears.
#lineIntThe line number on which it appears.
#columnIntThe column number in which it begins.
#functionStringThe name of the declaration in which it appears.

Log line number and method name in Swift

Swift provides some expressions in order to log, among others line number and method name:

Literal     Type    Value
#file String The name of the file in which it appears.
#line Int The line number on which it appears.
#column Int The column number in which it begins.
#function String The name of the declaration in which it appears.

For example:

func logFunctionName(string: String = #function) {
print(string)
}
func myFunction() {
logFunctionName() // Prints "myFunction()".
}

For more information, check the official documentation

(Reflection) Calling A Method With Parameters By Function Name In Swift

First of all, as you noted Swift doesn't have full reflection capabilities and rely on the coexisting ObjC to provide these features.

So even if you can write pure Swift code, you will need Solution to be a subclass of NSObject (or implement NSObjectProtocol).

Playground sample:

class Solution: NSObject {

@objc func functionName(greeting: String, name: String) {
print(greeting, name)
}

}

let solutionInstance = Solution() as NSObject
let selector = #selector(Solution.functionName)
if solutionInstance.responds(to: selector) {
solutionInstance.perform(selector, with: "Hello", with: "solution")
}

There are other points of concern here:

  • Swift's perform is limited to 2 parameters
  • you need to have the exact signature of the method (#selector here)

If you can stick an array in the first parameters, and alway have the same signature then you're done.
But if you really need to go further you have no choice than to go with ObjC, which doesn't work in Playground.

You could create a Driver.m file of the like:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

id call (NSObject *callOn, NSString *callMethod, NSArray <NSObject *>*callParameters)
{
void *result = NULL;
unsigned int index, count;

Method *methods = class_copyMethodList(callOn.class, &count);
for (index = 0; index < count; ++index)
{
Method method = methods[index];

struct objc_method_description *description = method_getDescription(method);
NSString *name = [NSString stringWithUTF8String:sel_getName(description->name)];
if ([name isEqualToString:callMethod])
{
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:description->types];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];

NSObject *parameters[callParameters.count];
for (int p = 0; p < callParameters.count; ++p) {
parameters[p] = [callParameters objectAtIndex:p];
[invocation setArgument:¶meters[p] atIndex:p + 2]; // 0 is self 1 is SEL
}
[invocation setTarget:callOn];
[invocation setSelector:description->name];
[invocation invoke];
[invocation getReturnValue:&result];
break;
}
}
free(methods);

return (__bridge id)result;
}

Add it to a bridging-header (for Swift to know about what is in ObjC):

// YourProjectName-Bridging-Header.h
id call (NSObject *callOn, NSString *callMethod, NSArray *callParameters);

And call it with a Solution.swift like this:

import Foundation

class Solution: NSObject {

override init() {
super.init()
// this should go in Driver.swift
let result = call(self, "functionNameWithGreeting:name:", ["Hello", "solution"])
print(result as Any)
}

@objc
func functionName(greeting: String, name: String) -> String {
print(greeting, name)
return "return"
}

}

output:

Hello solution
Optional(return)

Edit: compilation

To compile both ObjC and Swift on the command line you can first compile ObjC to an object file:

$ cc -O -c YouObjCFile.m

Then compile your Swift project with the bridging header and the object file:

$ swiftc -import-objc-header ../Your-Bridging-Header.h YouObjCFile.o AllYourSwiftFiles.swift -o program

working sample

How to identify calling method from a called method in swift

I worked out a way to do this, for Swift code anyway:

Define a String parameter callingFunction and give it a default value of #function. Do not pass anything from the caller and the compiler provides the calling function name.

Building on @Anu.Krthik's answer:

func someCalculation (parameter: String, callingMethod: String = #function ) {
print("In `\(#function)`, called by `\(callingMethod)`")
}

func foo(string: String) {
someCalculation(parameter: string)
}

foo(string: "bar")

The above prints

In `someCalculation(parameter:callingMethod:)`, called by `foo(string:)`

However, beware that this technique can be subverted if the caller provides a value for the callingFunction parameter. if you call it with:

func foo(string: String) {
someCalculation(parameter: string, callingMethod: "bogusFunctionName()")
}

You get the output

In `someCalculation(parameter:callingMethod:)`, called by `bogusFunctionName()`

instead.

Naming Convention for Methods and Properties in Swift. Can I use get, set and is Keywords?

The rules are all outlined here.

For get, that clearly violates the "Omit needless words" rule. If a method returns something, the call site will know that it is used to get some value. You don't need to repeat that idea. You can consider turning this into a computed property if no parameters are required.

For set, it might be appropriate sometimes. If your method only need one parameter and there is a corresponding getter,

func getFoo() -> Int {
...
}

func setFoo(_ foo: Int) {
...
}

That's a pretty good sign that this can be turned into a computed property:

var foo: Int {
get { ... }
set { ... }
}

A good example where it is appropriate to have set is the UIButton.setTitle method. It takes in two parameters, so a computed property wouldn't work.

For is, that clearly conforms to the rule "Uses of Boolean methods and properties should read as assertions about the receiver". So yes, you should use it for boolean members.



Related Topics



Leave a reply



Submit