I don't think that syntactic sugar is the way to go here. I will argue that there is much more beautiful functional way to do it with ability to add any customisation:
func weak<T: AnyObject, U>(
_ obj: T,
in f: @escaping (T) -> (U) -> Void
) -> (U) -> Void {
{ [weak obj] u in
guard let obj = obj else { return }
f(obj)(u)
}
}
And the usage is pretty straightforward:
class Foo {
func handler(data: Int) { print("Data: \(data)") }
func perform(data: Int) {
// strong capture
performAsync(with: data, completion: self.handler(data:))
// weak capture
performAsync(with: data, completion: weak(self, in: Foo.handler))
}
}
What is nice about this, is that you can add whatever customisation points you want. You want unowned? Cool!
func unowned<T: AnyObject, U>(
_ obj: T,
in f: @escaping (T) -> (U) -> Void
) -> (U) -> Void {
{ [unowned obj] u in
f(obj)(u)
}
}
// Usage
performAsync(with: data, completion: unowned(self, in: Foo.handler))
You want default if self is dead? That is also possible:
func weak<T: AnyObject, U, Z>(
_ obj: T,
default: Z,
in f: @escaping (T) -> (U) -> Z
) -> (U) -> Z {
{ [weak obj] u in
guard let obj = obj else { return default }
return f(obj)(u)
}
}
// Usage
performAsync(with: data, completion: weak(self, default: -1, in: Foo.handler))
It is also possible if you want to write inplace closure instead of weird (self) -> (arg) -> T thing:
func weak<T: AnyObject, U, Z>(
_ obj: T,
in f: @escaping (T, U) -> Z
) -> (U) -> Z {
{ [weak obj] u in
guard let obj = obj else { return default }
return f(obj, u)
}
}
// Usage
performAsync(with: data, completion: weak(self) { $0.handle($1) })
Unfortunately, in last case you are loosing nice trailing closure sugar.
It would be nice if we had variadic generics so we could write it once for any number of arguments, but for real use cases it is very rare to have callbacks with more than 2 or 3 arguments, so you need some boilerplate, but not too much.
The beauty of this solution is that it is fully in your control, there is no special compiler magic behind. It would be great, if we, as a community, will address things like generalisation features more than another special-case-sugar addition.