Swizzling is a classical trick in Objective-C runtime. We can replace some implementation at runtime by using swizzling. It is the most awesome feature in Cocoa development, and the most dangerous skill at the same time since it may break things.

In Objective-C, the method invoking goes to dispatch table, and the selector will be used to find an implementation. So if we replace the implementation of a selector, we are able to "re-implement" this method. If you do not know this, just think there is a dictionary storing methods for a class, in which the keys are selectors (or the name of method), and the values are what to do when this method is called. At Objective-C runtime, we just say the name of the method we want to execute, and find out the value in the dictionary with this name. Swizzling will use a new value to replace the old one, then we can do other things without changing the original code.

It is not so regularly used in app development. However, when you want to change some basic behaviors, swizzling could be very convenient. Take an example that we have a huge project with lots of UIButton. Someday if there is a need to count the button click event in the entire app (I know it might be a bit strange, but you will never know the next feature). For a poor skill guy, he or she would think it easy: "Oh, just use a static counter and add 1 to it in all button click methods!".

It is definitely the worst idea to do it. Of course we could find ALL button click, and count it. But how can you maintenance it later? Can you be sure all clicks are found? Another way would be creating a subclass of UIButton, and override the click event to count. It seems better, but we also have to replace all buttons in our project with the new class. It is nightmare as well.

This is the time swizzling to rescue. We can swizzle ALL click event of UIButton in a global scope to solve it forever in this project. No find & replace in existing code, no missed case, and no additional maintenance pressure.

In Swift, we can use the runtime of Objective-C to do swizzling as well. For the demand above, just use an extension of UIButton:

extension UIButton {
    class func xxx_swizzleSendAction() {
        struct xxx_swizzleToken {
            static var onceToken : dispatch_once_t = 0
        }
        dispatch_once(&xxx_swizzleToken.onceToken) {
            let cls: AnyClass! = UIButton.self

            let originalSelector = #selector(sendAction(_:to:forEvent:))
            let swizzledSelector = #selector(xxx_sendAction(_:to:forEvent:))

            let originalMethod =
                        class_getInstanceMethod(cls, originalSelector)
            let swizzledMethod =
                        class_getInstanceMethod(cls, swizzledSelector)

            method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }

    public func xxx_sendAction(action: Selector,
                                   to: AnyObject!,
                             forEvent: UIEvent!)
    {
        struct xxx_buttonTapCounter {
            static var count: Int = 0
        }

        xxx_buttonTapCounter.count += 1
        print(xxx_buttonTapCounter.count)
        xxx_sendAction(action, to: to, forEvent: forEvent)
    }
}

In the method of xxx_swizzleSendAction, we can get the selectors for both original method (sendAction:to:forEvent:) and the customized one (xxx_sendAction:to:forEvent:). Then exchange the implementation of them by the runtime function method_exchangeImplementations. In the implementation of xxx_sendAction:to:forEvent:, 1 is added first and then printed out. At last, there is an invoking on xxx_sendAction:to:forEvent:. It seems that we are calling the method itself here and may lead an infinity loop. However, remember we have already exchanged the original method with this. So it will call the original method here, to keep performing the default behaviors.

At last, we need to call xxx_swizzleSendAction when the app started. In Objective-C, we often do it in the +load method of a category. But the extension in Swift is quite different with category in Objective-C. Extension is not loaded at runtime, so there is not possible to use or overwrite load. We need a helper type to make the swizzling happen:

class Swizzler: NSObject {
    override class func load() {
        UIButton.xxx_swizzleSendAction()
    }
}

Now, all button click event sending will be from our swizzled method. Every time you click a button, you should be able to see a counting log in console now.

Swizzling is using dynamic dispatch of Objective-C runtime, so we can use it seamlessly for subclass of NSObject. But for Swift types which do not use Objective-C runtime by default, there is no dynamic dispatch method list. If we need to swizzle for Swift class, we need to add dynamic to both the original method and replace method, to indicate that they follow dynamic dispatch. For more about it, please refer to @objc and dynamic.

A> There is also another way to do it without Objective-C runtime. Swift is using a concept which is closed to "function pointer" to call a method under the hood. We might be able to replace it to "swizzle" a Swift method. But it beyonds the scope of this book. If you have an interest in it, you can have a look at SWRoute and how to hook a method in Swift.