Sunday, November 5, 2017

Swift 4 Method Swizzling (Part 2/2)

In the Swift 4 Method Swizzling (Part 1/2) post, We have seen the approach of using Base classes for a notification needed 
whenever a view appears, Which resulted in code redundancy.

In this post, Let’s see how that can be solved using Method Swizzling.

Take an extension of UIViewController and apply swizzling for it’s viewWillAppear method.

--------------------------------------------------------------------------

extension UIViewController {

    func newViewWillAppear(_ animated: Bool) {
        self.newViewWillAppear(animated) //Incase we need to override this method
        print("Display Notification!")
    }
    
    static func swizzleViewWillAppear() {
         //Make sure This isn't a subclass of UIViewController, So that It applies to all UIViewController childs
        if self != UIViewController.self {
            return
        }
        let _: () = {
            let originalSelector = #selector(UIViewController.viewWillAppear(_:))
            let swizzledSelector = #selector(UIViewController.newViewWillAppear(_:))
            let originalMethod = class_getInstanceMethod(self, originalSelector)
            let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
            method_exchangeImplementations(originalMethod!, swizzledMethod!);
        }()
    }    
}

--------------------------------------------------------------------------

Here, I am swapping, viewWillAppear with my custom Method newViewWillAppear using method_exchangeImplementations, Which is from ObjectiveC runtime.

--------------------------------------------------------------------------
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) 
--------------------------------------------------------------------------

At run time, calling viewWillAppear on UIViewController calls newViewWillAppear method, 
Where we will have the notification logic.

Now, In the child classes of UIViewControllers, super.ViewWillAppear, Calls the newViewWillAppear.

The beauty here is, The same will get called for UITableViewController and UINavigationController also as they are also extended from UIViewController.

Thus, Using Method Swizzling, There will not be any code redundancy. 

Unlike in Swift3x, In swift 4, We can not write this swizzling the initialise method, And we can call it in AppDelegate.

--------------------------------------------------------------------------

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        // Override point for customization after application launch.

        UIViewController.swizzleViewWillAppear()

        return true
    }

--------------------------------------------------------------------------

So, Whenever app launches, This swizzling applies and stays.

Hope this post is useful. Feel free to comment incase of any queries.