Dynamic Swift
We’ll go ahead and extend our
KVC
protocol and add our default implementation.extension KVC { func valueForKey(key : String) -> Any? { let mirror = reflect(self) for index in 0 ..< mirror.count { let (childKey, childMirror) = mirror[index] if childKey == key { return childMirror.value } } return nil } }
David Owens II has a different implementation that also handles setting values:
As you might be gathering, the basic premise is to use a backing store to maintain our values and type information. The implementation can then verify that the data coming in is correct.
But wait… if we can shadow the functions and replace them with our own implementations, then this means that method swizzling is back on the table!
There are several caveats, though.
Remember that reflection in Swift is currently read-only and there’s no way to modify your program at runtime (with Objective-C-derived classes being an exception as you can still
class_addMethod()
in Swift).
Brent Simmons asks how to instantiate an arbitrary class at runtime.
The same way as C++, an ugly switch statement!
On the latest Swift you have to add the word “init” to make this more explicit. Then it works. e.g:
let a:P = clsA.init(i:1)
Jim takes this one step further: what if all you have is the class name as a string?
Alas, that’s currently only possible using Objective-C classes. There is no NSClassFromString()
for Swift. [Update (2015-07-23): Actually, there is. See the comments below.]
Xcode 7 Beta 4 is out and it is a doozy! One of the changes is that
performSelector
is now available from Swift. Now, this isn’t going to make your Swift types dynamic all of a sudden. However, what it does open the door for, is writing both your ObjC-style code and your Swift code all in the same language: Swift.
Here’s the deal I’m willing to make: to make my code more elegant (read: smaller, easier to maintain, reusable), I’m willing to let it crash if I’ve configured things incorrectly.
[…]
So my case is that Swift itself needs things like this. If not a clone of KVC, then something very much like it.
When Apple announced the Swift programming language in 2014, there were questions about the future of frameworks that depended heavily on the dynamic nature of Objective-C. Frameworks such as Core Data marshall objects with type dependencies determined at runtime. The strict type checks required by the Swift compiler appeared in conflict with Core Data.
[…]
It turns out the answer to one question begat the solution to the other. By turning this class extension into a protocol and with a clever use of
typealias
, I resolved the insidious casting problem while maintaining type safety. Protocols to the rescue!
In other words, there are ways to use Core Data from Swift in a type-safe way. But it would still be impossible to write Core Data itself in pure Swift.
9 Comments RSS · Twitter
Michael,
I have a post at
http://www.spanware.com/blog/index.html
which describes a truly dynamic way to create an object factory in Swift whose subclasses are unknown at runtime.
@David Cool. But is it documented that NSClassFromString()
can look up Swift classes if you prefix their names? Seems like you are relying on an implementation detail.
Very interesting that NSClassFromString() actually works on native-Swift classes. I wouldn't even have thought to try that as I would have been sure that it wouldn't work.
@Michael, I'm pretty sure the ModuleName.ClassName is standard.
Maybe the Swift team is unifying the runtime models more than I had anticipated.
@DavidOwens Yes, I think the naming is standard. My question is: are they intentionally unifying the runtime models? Or does this accidentally work because they reused some code? Any idea what happens if you do this with generics?
This is an intentional part of the design, and should have been working since Swift 1 for simple swift classes.