Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

swift - is it possible to create a keypath to a method?

Tags:

swift

Is it possible to create a keypath referencing a method? all examples are paths to variables.

I'm trying this:

class MyClass {
    init() {
        let myKeypath = \MyClass.handleMainAction
        ...
    }
    func handleMainAction() {...}
}

but it does not compile saying Key path cannot refer to instance method 'handleMainAction()

like image 718
Martin Massera Avatar asked Nov 15 '17 18:11

Martin Massera


People also ask

What is the use of keypath in Swift?

Because closures of the form: (Root Type ) -> Value are common in Swift, Key path as functions is one of the features added to swift 5.2. it allows keypath to automatically be promoted and used wherever a functions of the form (Root ) -> Value are expected.

What is a keypath and how do I use it?

Key paths essentially let us reference any instance property as a separate value. As such, they can be passed around, used in expressions, and enable a piece of code to get or set a property without actually knowing which exact property its working with. Key paths come in three main variants: KeyPath: Provides read-only access to a property.

What is the use of writablekeypath?

WritableKeyPath works will Root of value types ( struct and enum ) and again allow us to read/write the keyPath property value of an instance taking into account the property should be var and the instance should be var also.

Can you use writable key path on an instance?

WritableKeyPath can use on both let and var for reference types. Constructing a key path using unsafe expressions can cause the same runtime error as using them on an instance. Here is an example using forced unwrapping expressions (!) and array subscript (index: Int) in key paths.


2 Answers

KeyPaths are for properties. However, you can do effectively the same thing. Because functions are first class types in swift, you can create a reference to handleMainAction and pass it around:

//: Playground - noun: a place where people can play

import UIKit
import XCTest
import PlaygroundSupport

class MyClass {
    var bar = 0

    private func handleMainAction() -> Int {
        bar = bar + 1
        return bar
    }

    func getMyMainAction() -> ()->Int {
        return self.handleMainAction
    }
}

class AnotherClass {
    func runSomeoneElsesBarFunc(passedFunction:() -> Int) {
        let result = passedFunction()
        print("What I got was \(result)")
    }
}


let myInst = MyClass()
let anotherInst = AnotherClass()
let barFunc = myInst.getMyMainAction()

anotherInst.runSomeoneElsesBarFunc(passedFunction: barFunc)
anotherInst.runSomeoneElsesBarFunc(passedFunction: barFunc)
anotherInst.runSomeoneElsesBarFunc(passedFunction: barFunc)

This will work fine, and you can pass "barFunc" to any other class or method and it can be used.

like image 190
David S. Avatar answered Oct 05 '22 08:10

David S.


You can use MyClass.handleMainAction as an indirect reference. It gives you a block that take the class instance as the input parameter, and returns corresponding instance method.

let ref = MyClass.handleMainAction  //a block that returns the instance method
let myInstance = MyClass()
let instanceMethod = ref(myInstance)
instanceMethod()                    //invoke the instance method

The point is you can pass around / store the method reference just like what you did with a key path. You just need to supply the actual instance when you need to invoke the method.

like image 24
Kelvin Avatar answered Oct 05 '22 07:10

Kelvin