2

When in a class, how to refer to the class itself when declaring closure parameters?

In the example below, what type to place in place of Self so that when constructing Foo, the closure parameter also becomes Foo and similarly for AnotherFoo?

class FooBase {
    init(completionHandler: (_ myself : Self)) {
        // ...
        self.completionHandler = completionHandler
    }

    var completionHandler : ((_ :Self) -> Void)?

    func strategyMethod() { ... }
}

class Foo : FooBase {
    // ...
    override func strategyMethod() {
        // do stuff
        completionHandler?(self)
    }
}

class AnotherFoo : FooBase {
    // ...
    override func strategyMethod() {
        // do other stuff
        completionHandler?(self)
    }
}

func useFoos {
    let foo = Foo(completionHandler: {(me : Foo) in
        // ...
    })
    let anotherFoo = AnotherFoo(completionHandler: {(me : AnotherFoo) in
        // ...
    })
}
adib
  • 8,285
  • 6
  • 52
  • 91
  • Uprooted, because I don't think you can. Hopefully the true gurus here can have a better answer. In terms of class hierarchy, this is sideways, correct? Can this be done in any language? –  Feb 20 '17 at 23:29
  • Related (dupe?): [Self in init params](http://stackoverflow.com/q/40055862/2976878). But your configuration is unsafe anyway – you cannot possibly have a stored property of type `((Self) -> Void)?` in a non-final class. In `Foo`, it could hold a value of type `(Foo) -> Void`. But if you then upcast to `FooBase`, it's now statically typed as `(FooBase) -> Void`, meaning you could call it with an `AnotherFoo` parameter – which would be illegal (`AnotherFoo` ≠ `Foo`). – Hamish Feb 21 '17 at 00:03
  • To be *perfectly* correct - I upvoted and spell correct made it uprooted. Apologies. –  Feb 21 '17 at 00:43
  • 1
    This feature is documented in this [accepted proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0068-universal-self.md), but it hasn't yet been implemented. – Alexander Feb 21 '17 at 01:09
  • 1
    @Alexander As far as I can see, that proposal only aims to introduce `Self` into the bodies of class instance members, as well as adding support for value types – I don't *believe* it covers the use of `Self` as a parameter of a closure parameter to a method (but I guess we'll have to wait and see exactly what its implementation will bring). – Hamish Feb 21 '17 at 01:27
  • @Hamish Fair point, but I doubt they'd do one without the other – Alexander Feb 21 '17 at 01:30

1 Answers1

1

I don't think Swift allows you to do what you want, but you can get close.

Use FooBase as the type, but in the closures you pass to the init functions, cast to the type that you know the parameter is:

class FooBase {
    init(completionHandler: @escaping (_ myself : FooBase) -> Void) {
        // ...
        self.completionHandler = completionHandler
    }

    var completionHandler : ((_ myself:FooBase) -> Void)?

    func strategyMethod() {
    }
}

class Foo : FooBase {
    // ...
    override func strategyMethod() {
        // do stuff
        completionHandler?(self)
    }
}

class AnotherFoo : FooBase {
    // ...
    override func strategyMethod() {
        // do other stuff
        completionHandler?(self)
    }
}

func useFoos() {
    let foo = Foo(completionHandler: {(myself_in : FooBase) in
        // This cast should always succeed when the closure is used as intended
        if let myself = myself_in as? Foo {
            // ...
        }
    })
    let anotherFoo = AnotherFoo(completionHandler: {(myself_in : FooBase) in
        // This cast should always succeed when the closure is used as intended
        if let myself = myself_in as? AnotherFoo {
            // ...
        }
    })
}
Gary Makin
  • 3,109
  • 1
  • 19
  • 27