That seems to have a comparable result - but where and when would those properties be set, and how would they deal with self?
It would be necessary that the closures are static so that every instance will adopt the changed behavior, wouldn't it?
It's definitely a lot more work than the method outlined in the proposal, but unless someone wants to use dynamic on a large number of methods, I think this wouldn't be to bad (and you could use code generators for the boilerplate).
It's not a bug, it's a feature - per-instance swizzling
no but seriously, those are some fair points. I wonder if dynamic methods couldn't be exposed as mutable closures somehow using another spelling (e.g. MyType.DynamicMethods.doSomething = { ... }). That could allow you to ignore the self argument when calling as a method and allow re-swizzling at runtime.
Imho it's still an option... I don't know how big of a problem @objc really is in this context, and how many people would need to write stuff like
open class Swizzled {
static public var xClosure: (Swizzled, Int) -> Int = {
return $1
}
public func swizzledProtocolRequirementX(parameter: Int) -> Int {
return Swizzled.xClosure(self, parameter)
}
}
Another issue of this solution is that without +initialize, somebody has to actually do the "swizzling"... but I hope this can be solved in the future with some reflection & metaprogramming - maybe even before @objc is fully deprecated ;-)
I will updated the pitch to clarify this. Your example will be rejected because the generic signature does not match.
The @dynamicReplacement(for:) attributes indicates which dynamic declaration is replaced by the new declaration. This declaration must have the same type -- including generic signature and requirements -- and be on an extension of the owning type or at module scope for dynamic non-member declarations.
// Module A
extension Set where Element: SomeProtocol {
dynamic func foo() { }
dynamic func bar() { }
}
// Module B
extension Set {
@ dynamicReplacement(for: "foo()")
dynamic func myFoo() { } // ERROR: signature of myFoo doesn't match signature of foo
}
extension Set where Element: SomeProtocol {
@dynamicReplacement(for: "bar()")
dynamic func myBar() { } // okay: signatures match
}
// ModuleA
class MyThing<T> {
static dynamic func doSomething() -> Int { return -1 }
}
// ModuleB
extension MyThing where T: MyProtocol {
@dynamicReplacement(for: doSomething)
func doSomethingVariant1() -> Int { return 42 } / ERROR: signature of doSomethingVariant1 doesn't match signature of doSomething
}
Seems like this has been merged into master a while ago, but since the attribute is marked UserInaccessible, I guess it'll be a while before its publicly available?
I now hope that this feature stay only available for Apple since there is no clear/real use case for it(outside of Apple), FWIW the consensus seem to be more against it than for it and for the record yes Iām biased Iām against it.
I just came across this thread and thought that some of you might be interested in an experimental package I made a little while ago for replacing Swift functions/methods at run time: swift-mixin. Keep in mind that it was purely an 'academic' endeavour, so it's very unstable, only works on x86, and doesn't work for class methods as of 5.6. I also probably won't be addressing any of those issues because I only made it because it seemed like an interesting challenge (I wouldn't recommend actually using it in a legitimate project).
As an aside, there is a lower level way of "swizzling" any Swift method, "iinterposing" explained here. Designing it into the language in as general and powerful a form is likely to be difficult.