Sunday, November 5, 2017

Swift 4 Method Swizzling (Part 1/2)

Method Swizzling is the process of changing the implementation of methods of a class at runtime.

Let’s see some use cases of Method Swizzling.

I want to do something before all of my ViewController’s ViewWillAppear, 

Like,

  • I need to display an alert to the user If the auth token expires and user has to Re-Login
  • I need to display an offer to the user as a Banner whatever may be the screen that user presently is in.


In these cases, Actually we can go with a Base class and write our logic in the Base class.

But this is not the correct approach. 

Because, We need to write Base classes for not only UIViewController, But also for UITableViewController, UINavigationController, UITabBarController and all which we use in our app.

If we go with Base classes, Our code will be redundant and there will be multiple base classes for the same piece of code.

Let’s see a code example of Base class approach, And then we will see the how Method Swizzling solves that without code redundancy.

I have two View Controllers.

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

class ViewController: BaseViewController {
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    }
    
}

class ViewController2: BaseViewController {
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    }    
}

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

In my Base class I will just write the log and consider that as my logic I needed to get executed before my ViewWillAppear.

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

class BaseViewController: UIViewController {

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("UIViewController - Display Notification!")
    }
    
}

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

We are good here, As both View Controllers are Inherited from BaseViewControllerOur notification will get fired at the time of these controllers viewWillAppear.

Now, There is a TableViewController.

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

class TableViewController: UITableViewController {
    
    let users = ["user1", "user2", "user3", "user4"]

    // MARK: - Table view data source
    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return users.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cellID", for: indexPath)
        cell.textLabel?.text = users[indexPath.row]
        return cell
    }
    
}

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

Obviously, I can not extend it from BaseViewController, Cause, If I do, We will end up with errors like shown below.



















That means, UITableViewController will lose it’s characteristics If we go with BaseViewController as a base class and there is no point in taking a UITableViewController

Same applies to UINavigationController.

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

class NavigationController: UINavigationController {
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    }
    
}

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

If we take BaseViewController as base class, UINavigationController loses it’s characteristics and your push and pop will not work.

So for UITableViewController and UINavigationControllers also, We need to take Base Classes.

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

class BaseTableViewController: UITableViewController {

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("UITableViewController - Display Notification")
    }  
}

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

Such a bad approach with such a code redundancy.

In the next tutorial, Let’s see how this can be solved using Method Swizzling.

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