Method Swizzling in Swift

I've heard a lot of commentary that at least some areas of Swift are a step backward. These tend to fall into a couple of categories

  • Fundamental differences: Single file for class declarations is a common one here. These are things where Swift is just fundamentally different. I think these things are highly unlikely to change. If you don't like these differences you can always go to Android. Where they use Java, and the class definition is all in one file.
  • Syntax and Style: Representative examples include the lamentation of verbose but meaningful method invocations, and Optionals. I think if these offend you... you are probably not going to like the next few years, but there is a chance that things will change. 
  • Missing Features: Exceptions, Access scopes, Reflection (see comment by Chris Lattner) , Dynamic Method forwarding, take your pick, Swift can't do many of the things above. I think in this case there is much cause for hope.There have been posts on the Apple forums to suggest at least Exceptions and access scopes are on their way (in the case of exceptions... a hint that something in that space is coming). 

However I think ,apart from of reflection, many of the missing features can easily be worked around. For example enumerations with tuples feel like a simple way of dealing with exceptional circumstances without having to bloat the language. So what about dynamic method forwarding? Actually, I think it's already there, and when a couple of the language features that are on their way (class vars) come along, they'll be easy. When (and if) reflection is introduced, they will even become easy to inject (as you'll see, I propose a solution that depends on the class being written with this in mind). 

Swizzle-able Classes in Swift

In this example I demonstrate just one of the things you can achieve with dynamic method invocation in Swift, but I believe it demonstrates the core principles required to apply it to the other use-cases. As already highlighted it does require your class to be built to be swizzled, but a quick sub-class (or possibly extension) would work around that. 

Before I show you how you can do it in XCode 6 Beta 3, let's take a look at how the class would look once class var's are implemented. 

class Swizzle {
    //You would want to at least protect this when we can
    class var _sayHello : (String)->String = { (who:String) -> String in
        return "Hello, \(who)"
    }
    
    func sayHello(who:String)->String{
        return Swizzle._sayHello(who)
    }
}

As you can see, I'm using closures at the class level for the "method" and providing a normal vanilla function at the instance level. It simply calls the closure defined at the class level. If we want to change the endpoint of the method, we can just change the class variable, supplying it a compatible closure. 

let immutableInstance = Swizzle()
var mutableInstance = Swizzle()

//Both print "Hello, World"
println(immutableInstance.sayHello("World"))
println(mutableInstance.sayHello("World"))

Swizzle._sayHello = { (who:String) -> String in
    return "Howdy, \(who)"
}

//Both print "Howdy, World"
println(immutableInstance.sayHello("World"))
println(mutableInstance.sayHello("World"))

It doesn't matter if it's a mutable or immutable instance, first we get two "Hello, World"'s then two "Howdy, World"'s. Simple. I have kept this example very clean and simple, but you should consider that scope will have a role to play here to. You might want the first parameter of a swizzled method to be the instance so that internal variables could be picked up. Again, that doesn't need to impact the caller (consumer), the instance function can insert itself at call time. 

Now, what about XCode 6 Beta 3? Well sadly we need a little bit more thunking. Without class variables we need a global variable to store the closure, although I've augmented that with programmatic class vars (which do work) so that migration is easy when class vars work (just delete the global and the custom getter and setter. 

Here's the whole thing

// Only needed until we have class variables
var __SwizzleSayHello = { (who:String) -> String in
    return "Hello, \(who)"
}
class Swizzle {
    //Only needed until we have class variables
    class var _sayHello : (String)->String { 
        get{ return __SwizzleSayHello } 
        set (swizzle) {__SwizzleSayHello = swizzle} 
    }
 
    func sayHello(who:String)->String{
        return Swizzle._sayHello(who)
    }
}

The Future

I have not seen anything about the chance of reflection in the language, but if we were to have it, this pattern could be made applicable to all classes with a simple extension. We'll have to wait and see. 

However, my point is that I don't think there are really any significant limitations posed by Swift as a language. I've yet to bump into much I just can't address with the exception of reflection.

We've made the example available in a Gist so it can be easily updated as Swift accrues features... 

What are you waiting for? Start Swift Swizzling!