Method Swizzling in Swift
Before I start verbalizing this post, I will repeat what other great developers have constantly repeated. Please beware while swizzling a method. If it's your own method you're swizzling, you should be fine. But if it's system method, make sure to call an original implementation from the swizzled method and add additional functionality in the swizzled method. (Like tracking, debugging or analytics code)
Before I start with an example, a brief description on what exactly we are going to do to achieve Method swizzling in Swift.
- We will create a class named
SwizzlableClass
whose method will be swizzled. - We will use the
dynamic
keyword for methods which are undergoing swizzling. (There are other mechanisms too, but this is considered to be the better one especially if we are not using Objective-C in conjunction with Swift.) - Get the
Method
s for both original and swizzled implementation. - Exchange the implementation with
method_exchangeImplementations(Method1, Method2)
Gotchas,
-
Based on the discussion on StackOverflow, load method is not supported in Swift. This enforces us to swizzle method in either
initialize
method of the class (override class func initialize()
) or indidFinishLaunchingWithOptions
method of theUIApplication
class. -
If you swizzle method in the
initialize
method, usedispatch_once
to make sure methods are swizzled exactly once. Otherwise, the second timeinitialize
is called, it will swap the methods one more time resetting methods to their original implementation
class SwizzlableClass: NSObject {
dynamic func originalMethod() {
print("Original Method")
}
dynamic func swizzledMethod() {
print("Swizzled Method")
}
override class func initialize() {
var onceToken: dispatch_once_t = 0
dispatch_once(&onceToken) {
let swizzlable = SwizzlableClass()
print(swizzlable.originalMethod()) // Prints Original Method
print(swizzlable.swizzledMethod()) // Prints Swizzled Method
let originalClass = SwizzlableClass.self
let originalMethod = class_getInstanceMethod(originalClass, #selector(SwizzlableClass.originalMethod))
let swizzledMethod = class_getInstanceMethod(originalClass, #selector(SwizzlableClass.swizzledMethod))
method_exchangeImplementations(originalMethod, swizzledMethod)
print(swizzlable.originalMethod()) // Prints Swizzled Method
print(swizzlable.swizzledMethod()) // Prints Original Method
}
}
}
Now the next time you call these methods, you can observe that their implementation has been swapped.
let swizzlable = SwizzlableClass()
swizzlable.originalMethod() // Prints Swizzled Method
swizzlable.swizzledMethod() // Prints Original Method
Personally I found Swift approach more terse than Objective-C. It's easier to remember too. However, I am not sure if I am going to experiment too much with method swizzling down the road