Swift Method Swizzling (almost!)

Previously I was wondering how to test precondition() and assert() in my code. Well, Nikolaj Schumacher suggested something clever: shadow the precondition() and assert() functions.

My implementation his suggestion can be found here: https://github.com/owensd/swift-fix/blob/master/Assertion.swift. There is still a slight problem with this version though: normally precondition() and assert() will terminate the execution of the function, but unless you use my throwing version of those functions, your code path will continue to execute. This may or may not be a problem for you, so beware.

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!

Here's an example:

Let's say, we have a scenario, where isEmpty on an array is just utterly broken. Too bad for us, right? Well… not exactly.

extension Array {
    var isEmpty: Bool {
        print("monkey patch!")
        return false
    }
}

let a: [Int] = []
a.isEmpty           // guess what this outputs? =)

Now, there is a caveat here… I don't know how to call the base implementation, so you're stuck with re-implementing the entire thing for member functions. For global functions though, we can fully qualify them to explicitly get the version we want.

Maybe someone else knows how to do that part?

Update, later that evening…

So, it turns out this really only works for the following:

  1. Global functions
  2. Properties on non-@objc types.

Oh well, and interesting hack. It does seem strange that some things can be shadowed and other things cannot.

Swift Method Swizzling (almost!)