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 Methods 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 in didFinishLaunchingWithOptions method of the UIApplication class.

  • If you swizzle method in the initialize method, use dispatch_once to make sure methods are swizzled exactly once. Otherwise, the second time initialize 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