iosswiftxcodeswift3swizzling

How to implement method swizzling swift 3.0?


How can I implement method swizzling in Swift 3.0 ?

I've read nshipster article about it, but in this code's chunk

struct Static {
    static var token: dispatch_once_t = 0
}

the compiler gives me an error

dispatch_once_t is unavailable in Swift: Use lazily initialized globals instead


Solution

  • First of all dispatch_once_t is unavailable in Swift 3.0. You can choose from two alternatives:

    1. Global variable

    2. Static property of struct, enum or class

    For more details, see that Whither dispatch_once in Swift 3

    For different purposes you must use different implementation of swizzling

    • Swizzling CocoaTouch class, for example UIViewController;
    • Swizzling custom Swift class;

    Swizzling CocoaTouch class

    example swizzling viewWillAppear(_:) of UIViewController using global variable

    private let swizzling: (UIViewController.Type) -> () = { viewController in
    
        let originalSelector = #selector(viewController.viewWillAppear(_:))
        let swizzledSelector = #selector(viewController.proj_viewWillAppear(animated:))
    
        let originalMethod = class_getInstanceMethod(viewController, originalSelector)
        let swizzledMethod = class_getInstanceMethod(viewController, swizzledSelector)
    
        method_exchangeImplementations(originalMethod, swizzledMethod) }
    
    extension UIViewController {
    
        open override class func initialize() {
            // make sure this isn't a subclass
            guard self === UIViewController.self else { return }
            swizzling(self)
        }
    
        // MARK: - Method Swizzling
    
        func proj_viewWillAppear(animated: Bool) {
            self.proj_viewWillAppear(animated: animated)
    
            let viewControllerName = NSStringFromClass(type(of: self))
            print("viewWillAppear: \(viewControllerName)")
        } 
     }
    

    Swizzling custom Swift class

    To use method swizzling with your Swift classes there are two requirements that you must comply with (for more details):

    And example swizzling method of custom Swift base class Person

    class Person: NSObject {
        var name = "Person"
        dynamic func foo(_ bar: Bool) {
            print("Person.foo")
        }
    }
    
    class Programmer: Person {
        override func foo(_ bar: Bool) {
            super.foo(bar)
            print("Programmer.foo")
        }
    }
    
    private let swizzling: (Person.Type) -> () = { person in
    
        let originalSelector = #selector(person.foo(_:))
        let swizzledSelector = #selector(person.proj_foo(_:))
    
        let originalMethod = class_getInstanceMethod(person, originalSelector)
        let swizzledMethod = class_getInstanceMethod(person, swizzledSelector)
    
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }
    
    extension Person {
    
        open override class func initialize() {
            // make sure this isn't a subclass
            guard self === Person.self else { return }
            swizzling(self)
        }
    
        // MARK: - Method Swizzling
    
        func proj_foo(_ bar: Bool) {
            self.proj_foo(bar)
    
            let className = NSStringFromClass(type(of: self))
            print("class: \(className)")
        }
    }