Get the name of class & function sent to another class [duplicate] - ios

here is a scenario
func callingMethod_A {
self.someCalculation()
}
func callingMethod_B{
self.someCalculation()
}
func someCalculation{
//how to find who called this method? is it callingMethod_A or _B at runtime?
//bla bla
}
how can we get the method name that called it during run time.
thank you.

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.

You can use Thread.callStackSymbols like this
func callingMethod_A() {
self.someCalculation()
}
func callingMethod_B(){
self.someCalculation()
}
func someCalculation(){
let origin = Thread.callStackSymbols
print(origin[0])
print(origin[1])
}

Related

swift protocol conformance with duplicate function names

class A {
func test(string: String, another defaultValue: String = "") {
///...
}
}
class B {
func test(string: String, different defaultValue: Bool = true) {
///...
}
}
protocol Test {
func test(string: String)
}
extension A: Test {
func test(string: String) {
self.test(string: string)
}
}
extension B: Test {
func test(string: String) {
self.test(string: string)
}
}
When I do this I get the following error
Function call causes an infinite recursion
How to confirm to the protocol Test to the classes which have similar function names
When A or B conform to Test, there is no way for the program to know which .test you're calling, cause the internal .test methods in A & B have default values.
To resolve the ambiguity, you can be specific:
extension A: Test {
func test(string: String) {
self.test(string: string, another: "")
}
}
extension B: Test {
func test(string: String) {
self.test(string: string, different: true)
}
}
You have to call class methods with full signature:
extension A: Test {
func test(string: String) {
self.test(string: string, another: "")
}
}
extension B: Test {
func test(string: String) {
self.test(string: string, different: true)
}
}
As swift allows method overloading, you may use the same function name with different signatures.
In your example class methods have different signatures, comparing to the protocol methods, so it is fine. However class methods have default values and can be called without full signature and in this case it is ambiguous as the signature become the same.

Passing a generic type to a function with generic constraint

I have a protocol with an associated Type
protocol ProtocolA {
associatedType someType
}
Now i have two generic functions
func funcA<someType>(_ data:someType) {
funcB(data) // cannot call
}
func funcB<someType:ProtocolA>(_ data:someType) {
}
I have been trying to call funcB from funcA, but it is not working I am getting the error
Instance method 'funcB' requires that 'someType' conform to 'ProtocolA'
Now i know for a fact that the Generic Type in funcA is conforming to ProtocolA. Is there anyway to also make sure that the compilers know it too ?
I cannot change the funcA method declaration to put a generic constraint as it is the requirement of another protocol.
I have tried using the some keyword in funcA by doing
var obj : some ProtocolA = data
However i am getting the error
Property declares an opaque return type, but cannot infer the underlying type from its initializer expression
Basically in short is there anyway i can call funcB from funcA without changing funcA signature, however funcB signature can be changed to whatever is required
****EDIT*****Added More Information
funcA is called by the protocl
protocol CommonService {
func funcA<ModelType>(_ data:ModelType)
}
class CommonServiceImpl : CommonService {
func funcA<someType>(_ data:someType) {
funcB(data) // cannot call
}
func funcB<someType:ProtocolA>(_ data:someType) {
//SomeCode here required that someType must implement ProtocolA
}
}
ProtocolA is contained in a third party pod that cannot be changed.
*******Edit***********How i solved the problem
So thanks to #Mojtaba Hosseini in answers i got a really good idea on how to solve my problem.
I simply wrote an overloaded function in my CommonServiceProtocol
protocol CommonService {
func funcA<ModelType>(_ data:ModelType)
func funcA<ModelType>(_ data:ModelType) where ModelType:ProtocolA
}
class CommonServiceImpl : CommonService {
func funcA<someType>(_ data:someType) {
funcB(data) // cannot call
}
func funcA<someType>(_ data:someType) where ModelType:ProtocolA {
funcB(data) // can be called
}
func funcB<someType:ProtocolA>(_ data:someType) {
//SomeCode here required that someType must implement ProtocolA
}
}
I mean it is not a perfect solution but given the hard dependency on an associatedType using ProtocolA in a third party pod i would say it works alright and that is one of the reasons to avoid third party dependencies as much as possible.
Is there any way to also make sure that the compilers know it too?
You have to implement an overload for the funcA and constraint it:
func funcA<someType>(_ data: someType) {
/* funcB(data) */ cannot call
print("Not detected")
}
func funcA<someType>(_ data: someType) where someType: ProtocolA {
funcB(data) // can call ✅
print("Detected")
}
so calling funcA("") will result Not detected
but conforming to the protocol and calling the same function will result in Detected
// extension String: ProtocolA { typealias someType = String } // uncomment to see
funcA("")
As per your requirement, I think you can match the signature of your funcB with funcA. Refer to the code below:
func funcA<someType>(_ data:someType) {
funcB(data)
}
func funcB<someType>(_ data:someType) {
}
As shown in the above code, you can remove the type constraint for someType in funcB.
In functionB just add a parameter that will tell the compiler the type you expect, if you really want to be sure the type is your protocol add a check :
func transform<T>( data : T ){
guard let data = data as? Monster else {
print("data is not a monster type")
return
}
intoMonster(Monster.self, data: data)
}
func intoMonster<T> (_ type : T.Type , data : T){
}

How to identify calling method from a called method in swift

here is a scenario
func callingMethod_A {
self.someCalculation()
}
func callingMethod_B{
self.someCalculation()
}
func someCalculation{
//how to find who called this method? is it callingMethod_A or _B at runtime?
//bla bla
}
how can we get the method name that called it during run time.
thank you.
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.
You can use Thread.callStackSymbols like this
func callingMethod_A() {
self.someCalculation()
}
func callingMethod_B(){
self.someCalculation()
}
func someCalculation(){
let origin = Thread.callStackSymbols
print(origin[0])
print(origin[1])
}

cancelPreviousPerformRequests does not seem to work in Swift 3.0

As the title states, for some reason, the following (simplified) code is not working:
extension InputView: {
func updateTable(text: String) {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(loadPlaces(text:)), object: nil)
//NSObject.cancelPreviousPerformRequests(withTarget: self)
self.perform(#selector(loadPlaces(text:)), with: text, afterDelay: 0.5)
prevSearch = inputField.text!;
}
//Private wrapper function
#objc private func loadPlaces(text: String) {
print("loading results for: \(text)")
// locator?.searchTextHasChanged(text: text)
}
}
I call updateTable every time a user edits a UITextField, which calls localPlaces which calls a function that queries google's online places API (commented out). Unfortunately, the print line in loadPlaces is called after every single call to updateTable. From my visual inspection, it seems there is in fact a delay to the print statements, however, the old calls do not cancel. I've tried looking on a lot of StackOverflow threads but I couldn't find anything updated for Swift 3. Am I calling something incorrectly?
PS. If I instead use the commented out, single-argument, cancelPreviousPerformRequests. It works for some reason.
Edit: I have been able to replicate this error in a separate project. So I'm 100% sure that the above code is wrong. If you would like to replicate this error, open up a new iOS project and paste the following code into the default ViewController:
class InputView: UIView {
func updateTable(text: String) {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(loadPlaces(text:)), object: nil)
self.perform(#selector(loadPlaces(text:)), with: text, afterDelay: 0.5)
}
//Private wrapper function
#objc private func loadPlaces(text: String) {
print("loading results for: \(text)")
// locator?.searchTextHasChanged(text: text)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let input = InputView()
for i in 0..<200 {
input.updateTable(text: "Call \(i)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The explanation in Duncan C's answer is not appropriate for this case.
In the reference of cancelPreviousPerformRequests(withTarget:selector:object:):
Discussion
All perform requests are canceled that have the same target as aTarget, argument as anArgument, and selector as
aSelector.
So, when you have a line like:
<aTarget>.perform(<aSelector>, with: <anArgument>, afterDelay: someDelay)
You can cancel it with:
NSObject.cancelPreviousPerformRequests(withTarget: <aTarget>, selector: <aSelector>, object: <anArgument>)
only when all 3 things aTarget, aSelector and anArgument match.
Please try something like this and check what you see:
class InputView: UIView {
var lastPerformArgument: NSString? = nil
func updateTable(text: String) {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(loadPlaces(text:)), object: lastPerformArgument)
lastPerformArgument = text as NSString
self.perform(#selector(loadPlaces(text:)), with: lastPerformArgument, afterDelay: 0.5)
}
//Private wrapper function
#objc private func loadPlaces(text: String) {
print("loading results for: \(text)")
// locator?.searchTextHasChanged(text: text)
}
}
EDIT:
The first part of this answer is wrong. See the edit at the bottom for updated information. I'm leaving the original answer since the discussion might be useful.
It looks to me like there is a bug in the way NSObject maps Swift function names to selectors that is preventing this from working correctly. The only way I was able to get the cancelPreviousPerformRequests function to actually cancel the pending perform() is if the function does not have any parameters. If the function takes a single anonymous parameter or a named parameter then the cancelPreviousPerformRequests function does not cancel the pending perform(_:with:afterDelay:).
Another bug I've found: If you use a function with an anonymous parameter, e.g.:
func foo(_ value: String) {
print("In function \(#function)")
}
Then the result you see in the print statement is:
In function foo
You'll see the same thing if the function has 2, 3, or more anonymous parameters.
If you have a function with no parameters, you get a different result:
func foo() {
print("In function \(#function)")
}
That code will display the message:
In function foo()
(Note the parentheses after the function name.)
EDIT
Note that it seems I was wrong. Apparently the object parameter to cancelPreviousPerformRequests must match what was passed in. You can only pass object:nil to cancelPreviousPerformRequests if the selector was invoked with a nil argument.
To quote the docs:
The argument for requests previously registered with the
perform(:with:afterDelay:) instance method. Argument equality is
determined using isEqual(:), so the value need not be the same object
that was passed originally. Pass nil to match a request for nil that
was originally passed as the argument.

Gameplaykit GKState, swift func with two parameters

I'm sure that is a simple question for you.
How can I write a func with two parameters with one GKState?
UPDATE
Apple use
func willExitWithNextState(_ nextState: GKState)
If I use somefunc(state:GKState) works fine
while somefunc(state:GKState, string:String) does't work, why???
Other example
I've tried this:
class Pippo:GKState {}
//1
func printState (state: GKState?) {
print(state)
}
printState(Pippo) //Error cannot convert value of type '(Pippo).Type' (aka 'Pippo.Type') to expected argument type 'GKState?'
//2
func printStateAny (state: AnyClass?) {
print(state)
}
printStateAny(Pippo) //NO Error
//3
func printStateGeneral <T>(state: T?) {
print(state)
}
printStateGeneral(Pippo) //No Error
//4
func printStateAnyAndString (state: AnyClass?, string:String) {
print(state)
print(string)
}
printStateAnyAndString(Pippo/*ExpectedName Or costructor*/, string: "Hello") //ERROR
printStateAnyAndString(Pippo()/*ExpectedName Or costructor*/, string: "Hello") //ERROR cannot convert value of type 'Pippo' to expected argument type 'AnyClass?'
SOLUTION THANKS #0x141E
func printStateAnyAndString (state: GKState.Type, string:String) {
switch state {
case is Pippo.Type:
print("pippo")
default:
print(string)
}
}
printStateAnyAndString(Pippo.self, string: "Not Pippo")
Thanks for reply
If you want a parameter to be a class, use Class.Type or AnyClass
func printState (state: AnyClass, string:String) {
print(state)
print(string)
}
and use Class.self as the argument
printState(Pippo.self, string:"hello pippo")
Update
If your function definition is
func printState (state:GKState, string:String) {
if state.isValidNextState(state.dynamicType) {
print("\(state.dynamicType) is valid")
}
print(state)
print(string)
}
you'll need to pass in an instance of GKState (or a subclass of GKState) as the first argument, not the class/subclass itself. For example,
let pippo = Pippo()
printState (pippo, "Hello")
Throughout your sample code you have used AnyClass, whereas you should (probably) be using AnyObject. AnyClass refers to a class definition, whereas AnyObject is an instance of a class.
class MyClass { }
func myFunc1(class: AnyClass)
func myFunc2(object: AnyObject)
let myObject = MyClass() // create instance of class
myFunc1(MyClass) // myFunc1 is called with a class
myFunc2(myObject) // myFunc2 is called with an instance
You have also made most of your parameters Optionals with the "?", whereas it doesn't look required. For example:
printState(nil) // What should this do?

Resources