Swift: Determine What Object Called a Function

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.

in Xcode, how does the delegating object know WHICH method on the delegate object to call?

Inside UITextField will be code equivalent to:

self.delegate?.textfield(self, shouldChangeCharactersIn: rangeThatChanged, replacementString: stringThatChanged)

So, the text field code calls the specific function on its delegate, passing the required parameters including a reference to itself. This is no different to invoking any function on an object reference.

In respect of your 'Bonus question', the name of the delegate function is shouldChangeCharactersIn. This function indicates whether the change takes place by returning true or false. If it returns false then the change is discarded. If there is no change then the change action is not invoked.

How to get a method’s defining class’s name?

I don't believe what you're specifically asking for is possible. There's no way I know of to evaluate something in your caller's context implicitly, except the provided #-literals.

That said, if you will change your syntax slightly, you can get the effect you want.

public class Logger {
public let caller: String

public init(for caller: Any) {
self.caller = "\(type(of: caller))"
}

public func info(_ message: String, file: String = #file, line: Int = #line, function: String = #function) {
print("Function \(function) of \(caller) in file \(file) was called.")
}
}

Now, in objects that use the logger, they just need to create their own with a consistent name like log. Make sure your Logger is stateless, so it's ok that they get created on demand.

class MyLoggingThing {
var log: Logger { Logger(for: self) }

func doSomething() {
log.info("Let's do this")
}
}

// Function doSomething() of MyLoggingThing in file MyPlayground.playground was called.

You can make this a little nicer with an extension, and handle static methods:

protocol Logging {}
extension Logging {
static var log: Logger { Logger(for: self) }
var log: Logger { Logger(for: self) }
}

class MyLoggingThing: Logging {
static func doSomethingStatic() {
log.info("Even static")
}
func doSomething() {
log.info("Let's do this")
}
}

Note that static methods will show the type as MyLoggingThing.Type. That's good or bad, depending on what you want. If you don't like the extra .Type, you can add an extra Logger.init like this:

public init(for staticCaller: Any.Type) {
self.caller = "\(staticCaller)"
}

That will cause types to be evaluated as themselves rather than as their metatypes.

If your logger is stateful or has a central configuration, or other situation where lots of loggers might be a problem, you should split apart the stateful "engine" part from this front-end. You can also make log a lazy var or otherwise initialize it in init when self is available.

In my personal Logging module, I also have a global "root" Logger called Log (with a leading capital). That makes it easier for functions that might not want to name themselves (such as top level functions or closures).

Swift class object sends its value when calling a function

you can do something like this:

class Location {

private var _position: Double!

var position: Double {
return _position
}

init(position: Double) {

self._position = position
}

func distance (reference: Location) -> Double {
let point1 = self.position //or whatever leads to your value
let point2 = reference.position
//do what ever you have to do with both points e.g.
let result = point1 - point2
return result
}

}

let point1 = Location(position: 50.0)

let point2 = Location(position: 32.0)

now you can do your desired thing:

point1.distance(reference: point2)

// returns 18

(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 get the name of the current and calling function in dart?

import 'dart:mirrors';
...
MethodMirror methodMirror = reflect(functionOne).function;

See also https://github.com/dart-lang/sdk/issues/11916#issuecomment-108381556

This will only work in the Dart command line VM, but not in the browser or Flutter because there reflection is not supported.

Code generation solutions like https://pub.dartlang.org/packages/reflectable might work instead where reflection is not available.

https://github.com/dart-lang/sdk/issues/28372 seems related.



Related Topics



Leave a reply



Submit